/***********************************************************************
* mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.components.visibleComponents.shapes;
import java.util.ArrayList;
import javax.media.opengl.GL;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.mt4j.components.MTComponent;
import org.mt4j.components.TransformSpace;
import org.mt4j.components.bounds.IBoundingShape;
import org.mt4j.components.bounds.OrientedBoundingBox;
import org.mt4j.components.visibleComponents.AbstractVisibleComponent;
import org.mt4j.components.visibleComponents.GeometryInfo;
import org.mt4j.input.gestureAction.DefaultDragAction;
import org.mt4j.input.gestureAction.DefaultRotateAction;
import org.mt4j.input.gestureAction.DefaultScaleAction;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.MTColor;
import org.mt4j.util.animation.Animation;
import org.mt4j.util.animation.AnimationEvent;
import org.mt4j.util.animation.AnimationManager;
import org.mt4j.util.animation.IAnimationListener;
import org.mt4j.util.animation.MultiPurposeInterpolator;
import org.mt4j.util.math.ConvexQuickHull2D;
import org.mt4j.util.math.Matrix;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GLTexture;
import org.mt4j.util.opengl.GLTextureSettings;
import org.mt4j.util.opengl.GLTexture.EXPANSION_FILTER;
import org.mt4j.util.opengl.GLTexture.SHRINKAGE_FILTER;
import org.mt4j.util.opengl.GLTexture.TEXTURE_TARGET;
import org.mt4j.util.opengl.GLTexture.WRAP_MODE;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PImage;
/**
* Abstract superclass for all kinds of shapes defined by vertices.
*
* @author Christopher Ruff
*/
public abstract class AbstractShape extends AbstractVisibleComponent {
private static final Logger logger = Logger.getLogger(AbstractShape.class.getName());
static{
logger.setLevel(Level.ERROR);
SimpleLayout l = new SimpleLayout();
ConsoleAppender ca = new ConsoleAppender(l);
logger.addAppender(ca);
}
/** Default gesture actions. */
private static final IGestureEventListener defaultScaleAction = new DefaultScaleAction();
/** Default gesture actions*. */
private static final IGestureEventListener defaultRotateAction = new DefaultRotateAction();
/** Default gesture actions*. */
private static final IGestureEventListener defaultDragAction = new DefaultDragAction();
//Texture Stuff
/** The texture enabled. */
private boolean textureEnabled;
/** The texture mode. */
private int textureMode; // set defaults!
/** The texture image. */
private PImage textureImage;
/** The draw direct gl. */
private boolean drawDirectGL;
/** The use vb os. */
private boolean useVBOs;
/** The use display list. */
private boolean useDisplayList;
/** The geometry of this shape. */
private GeometryInfo geometryInfo;
// /** The bounds global vertices dirty. */
// private boolean boundsGlobalVerticesDirty;
/** The vertices global. */
private Vertex[] verticesGlobal;
/** global vertices dirty. */
private boolean globalVerticesDirty;
/**
* Creates a new shape with the vertices provided.
*
* @param vertices the vertices
* @param pApplet the applet
*/
public AbstractShape(Vertex[] vertices, PApplet pApplet) {
this(new GeometryInfo(pApplet, vertices), pApplet);
}
/**
* Creates a new geometry with the geometryInfo provided.
*
* @param geometryInfo the geometry info
* @param pApplet the applet
*/
public AbstractShape(GeometryInfo geometryInfo, PApplet pApplet) {
super(pApplet,"unnamed AbstractShape", /*null,*/ null);
//Initialize fields
this.drawDirectGL = MT4jSettings.getInstance().isOpenGlMode()? true : false;
this.useVBOs = false;
this.useDisplayList = false;
this.textureMode = PConstants.NORMALIZED;
this.setFillDrawMode(GL.GL_TRIANGLE_FAN);
// this.boundsGlobalVerticesDirty = true;
this.boundsAutoCompute = true;
this.setGeometryInfo(geometryInfo);
//Default
this.boundsBehaviour = BOUNDS_CHECK_THEN_GEOMETRY_CHECK;
this.globalVerticesDirty = true;//
this.setDefaultGestureActions();
}
/*
//FIXME TODO switch drawBounds! put draw() into IBoundingShape!
@Override
public void postDraw(PGraphics g) {
super.postDraw(g);
if (this.getBounds() instanceof OrientedBoundingBox){
OrientedBoundingBox b = (OrientedBoundingBox)this.getBounds();
b.drawBounds(g);
}
else if (this.getBounds() instanceof BoundsZPlaneRectangle){
BoundsZPlaneRectangle b = (BoundsZPlaneRectangle)this.getBounds();
b.drawBounds(g);
}
else if (this.getBounds() instanceof BoundingSphere){
BoundingSphere b = (BoundingSphere)this.getBounds();
b.drawBounds(g);
}
}
*/
/*
//Test for drawing bounding shape aligned to coordinate axis, like getWidth/getHeightRelativeToParent would return
@Override
public void postDrawChildren(PGraphics g) {
super.postDrawChildren(g);
if (this.getBounds() instanceof BoundsZPlaneRectangle){
BoundsZPlaneRectangle b = (BoundsZPlaneRectangle)this.getBounds();
// b.drawBounds(g);
g.pushMatrix();
g.pushStyle();
g.fill(250,150,150,180);
Vector3D[] v = b.getVectorsGlobal();
float[] minMax = ToolsGeometry.getMinXYMaxXY(v);
g.beginShape();
g.vertex(minMax[0], minMax[1], 0);
g.vertex(minMax[2], minMax[1], 0);
g.vertex(minMax[2], minMax[3], 0);
g.vertex(minMax[0], minMax[3], 0);
g.endShape();
//
g.popStyle();
g.popMatrix();
}
}
*/
//FIXME TEST
public static boolean createDefaultGestures = true;
/**
* Assigns the default gesture to this component, drag, rotate, scale.
* <br>Gets called in the constructor.
* Can be overridden in subclasses to allow other/more default gestures.
*/
protected void setDefaultGestureActions(){
// /*
if (createDefaultGestures){
this.registerInputProcessor(new RotateProcessor(this.getRenderer()));
this.setGestureAllowance(RotateProcessor.class, true);
// this.addGestureListener(RotateProcessor.class, defaultRotateAction);
this.addGestureListener(RotateProcessor.class, new DefaultRotateAction());
this.registerInputProcessor(new ScaleProcessor(this.getRenderer()));
this.setGestureAllowance(ScaleProcessor.class, true);
// this.addGestureListener(ScaleProcessor.class, defaultScaleAction);
this.addGestureListener(ScaleProcessor.class, new DefaultScaleAction());
this.registerInputProcessor(new DragProcessor(this.getRenderer()));
this.setGestureAllowance(DragProcessor.class, true);
// this.addGestureListener(DragProcessor.class, defaultDragAction);
this.addGestureListener(DragProcessor.class, new DefaultDragAction());
}
// */
}
//////////////// BOUNDING STUFF ///////////////////////////////
// /** The bounding shape. */
// private IBoundingShape boundingShape;
/** The Constant BOUNDS_ONLY_CHECK. */
public static final int BOUNDS_ONLY_CHECK = 1;
/** The Constant BOUNDS_CHECK_THEN_GEOMETRY_CHECK. */
public static final int BOUNDS_CHECK_THEN_GEOMETRY_CHECK = 2;
/** The Constant BOUNDS_DONT_USE. */
public static final int BOUNDS_DONT_USE = 3;
//FIXME RENAME, KEEP OLD ONES BUT AS DPERECATED
// BOUNDSBEHAVIOUR_USE_GEOMETRY
// BOUNDSBEHAVIOUR_USE_BOUNDS_AND_GEOMETRY
// BOUNDSBEHAVIOUR_USE_BOUNDS
//
// /** The Constant BOUNDS_ONLY_CHECK. */
// public static final int BOUNDS_ONLY_CHECK = 1;
//
// /** The Constant BOUNDS_CHECK_THEN_GEOMETRY_CHECK. */
// public static final int BOUNDS_CHECK_THEN_GEOMETRY_CHECK = 2;
//
// /** The Constant BOUNDS_DONT_USE. */
// public static final int BOUNDS_DONT_USE = 3;
/** The bounds picking behaviour. */
private int boundsBehaviour;
/** The bounds auto compute. */
private boolean boundsAutoCompute;
//TODO!!
// @Override
//INterface geometryNode mit getCenter getBounds etc? f�r Abstractshape und ShapeContainer?
// public void addChild(AbstractShape b){
//
// }
/**
* Sets the bounds behaviour. The behaviour influences
* calculations in methods like <code>getIntersectionLocal</code> (used in picking) and
* <code>getComponentContainsPointLocal</code>.
* Allowed values are:
* <ul>
* <li><code>AbstractShape.BOUNDS_ONLY_CHECK</code> <br>
* -> Uses the shape's bounding shape for the calculations <br>
* => faster, more inaccurate
* <li><code>AbstractShape.BOUNDS_DONT_USE</code> <br>
* -> Uses the shape's geometry for the calculations <br>
* => slower, more accurate
* <li><code>AbstractShape.BOUNDS_CHECK_THEN_GEOMETRY_CHECK</code> <br>
* -> Uses the shape's bounding shape first, and then also checks the geometry at picking. (Default)<br>
* => compromise between the other two
* </ul>
*
* @param boundsBehaviour the new bounds behaviour
*/
public void setBoundsBehaviour(int boundsBehaviour){
this.boundsBehaviour = boundsBehaviour;
}
/**
* Gets the bounds behaviour.
*
* @return the bounds behaviour constant
*/
private int getBoundsBehaviour(){
return this.boundsBehaviour;
}
//FIXME REMOVE THESE METHODS IN THE NEXT VERSION!
/**
* Sets the bounds picking behaviour.
* @param boundsPickingBehaviour the new bounds picking behaviour
*
* @deprecated
* Method was renamed! Use setBoundsBehaviour()!
*/
public void setBoundsPickingBehaviour(int boundsPickingBehaviour){
this.boundsBehaviour = boundsPickingBehaviour;
}
//FIXME REMOVE THESE METHODS IN THE NEXT VERSION!
/**
* Gets the bounds picking behaviour.
*
* @return the bounds picking behaviour
* @deprecated
* Method was renamed! Use getBoundsBehaviour()!
*/
private int getBoundsPickingBehaviour(){
return this.boundsBehaviour;
}
// /**
// * Sets the bounding shape.
// *
// * @param boundingShape the new bounding shape
// */
// public void setBoundingShape(IBoundingShape boundingShape){
// this.boundingShape = boundingShape;
// this.setBoundsGlobalDirty(true);
// }
//
// /**
// * Gets the bounding shape.
// *
// * @return the bounding shape
// */
// public IBoundingShape getBounds(){
// return this.boundingShape;
// }
//
// /**
// * Checks if is bounding shape set.
// * @return true, if is bounding shape set
// */
// public boolean hasBounds(){
// return this.boundingShape != null;
// }
//
// /**
// * Sets the bounding shape.
// *
// * @param boundingShape the new bounding shape
// */
// public void setBoundingShape(IBoundingShape boundingShape){
// super.setBoundingShape(boundingShape);
// this.setBoundsGlobalDirty(true);
// }
//
// //TODO REMOVE?
// /**
// * Sets the bounds global vertices dirty.
// *
// * @param boundsWorldVerticesDirty the new bounds world vertices dirty
// */
// private void setBoundsGlobalDirty(boolean boundsWorldVerticesDirty) {
// this.boundsGlobalVerticesDirty = boundsWorldVerticesDirty;
// IBoundingShape bounds = this.getBounds();
// if (bounds != null){
// bounds.setGlobalBoundsChanged();
// }
// }
//
@Override
public void setMatricesDirty(boolean baseMatrixDirty) {
/*
* Overridden, so the component is also informed of the need to update
* the bounds vertices
*/
if (baseMatrixDirty){
// this.setBoundsGlobalDirty(true);
this.globalVerticesDirty = true;
}
// System.out.println("Set pickmat dirty on obj: " + this.getName());
super.setMatricesDirty(baseMatrixDirty);
}
/**
* Sets the bounds auto compute.
*
* @param autoCompute the new bounds auto compute
*/
public void setBoundsAutoCompute(boolean autoCompute){
this.boundsAutoCompute = autoCompute;
}
/**
* Checks if is bounds auto compute.
*
* @return true, if is bounds auto compute
*/
public boolean isBoundsAutoCompute(){
return this.boundsAutoCompute;
}
/**
* Computes a default bounding box for the shape.
* This gets called after setting creating a shape and its setGeometryInfo method is called.
*/
protected IBoundingShape computeDefaultBounds(){
return new OrientedBoundingBox(this);
}
////////////////BOUNDING STUFF ///////////////////////////////
/**
* Sets a new geometryInfo with new vertices for this shape.
* <br>If running in OpenGL mode, this also creates new vertex buffers
* for openGL use and eventually new Vertex Buffer Objects or
* Displaylists depending on the objects settings!
* So DONT create them (buffers or vbos) on the geometryinfo yourself manually,
* prior to setting it here!
* <br>Also calls computeDefaultBounds() if setAutoComputeBounds() is true (default)
* to recreate the bounding shape.
* <br><strong>NOTE:</strong> Be aware, that an old geometryinfo of this shape may have
* created VBOs or displaylists on the gfx card which we should delete if not needed
* anywhere else!
*
* @param geometryInfo the geometry info
*/
public void setGeometryInfo(GeometryInfo geometryInfo){
if (this.isUseDirectGL()){
if (geometryInfo.getVertBuff() == null || geometryInfo.getStrokeColBuff() == null){
//new geometryinfo has no drawbuffers created yet -> create them!
geometryInfo.generateOrUpdateBuffersLocal(this.getStyleInfo());
}else if (this.geometryInfo != null && geometryInfo.equals(this.geometryInfo)){
// old geometryinfo is the same than the new one -> assumimg change -> create new buffers!
geometryInfo.generateOrUpdateBuffersLocal(this.getStyleInfo());
}else{
//the new geometryinfo already has opengl draw buffers and
//the old geometryinfo is null or not the same as the new one
//-> just use the new geometry's buffers without recreating!
//TODO do the same check with displaylists and vbos!??!
//
}
if (this.isUseVBOs()){
geometryInfo.generateOrUpdateAllVBOs();
}
if (this.isUseDisplayList()){
geometryInfo.generateDisplayLists(
this.isTextureEnabled(),
this.getTexture(),
this.getFillDrawMode(),
this.isDrawSmooth(),
this.getStrokeWeight());
}
}
this.geometryInfo = geometryInfo;
if (this.isBoundsAutoCompute()){
if (geometryInfo.getVertices().length >= 3){
this.setBounds(this.computeDefaultBounds());
}else{
// logger.error("Warning: could not compute bounds because too few vertices were supplied: " + this.getName() + " in " + this + " -> Setting boundingShape to null.");
this.setBounds(null);
}
}else{
this.setBounds(null);
}
//Sets the base matrix dirty, so that when inquiring info about
//vertices, they get updated first
// this.setLocalBaseMatrixDirty(true);
this.globalVerticesDirty = true;
}
/**
* Gets the geometry info. The geometryinfo contains the
* geometric information of this shape by managing the shapes
* vertices, OpenGL vertex buffer objects and OpenGL display list.
*
* @return the geometry info
*
* the geometry information object of that shape
*/
public GeometryInfo getGeometryInfo() {
return this.geometryInfo;
}
/**
* Sets new vertices for that shape.
* and generates new vertex arrays for opengl mode.
* <li>Re-computes and sets the shapes default bounding shape.
*
* @param vertices the vertices
*/
public void setVertices(Vertex[] vertices){
this.getGeometryInfo().reconstruct(
vertices,
this.getGeometryInfo().getNormals(),
this.getGeometryInfo().getIndices(),
this.isUseDirectGL(),
this.isUseVBOs(),
this.getStyleInfo());
if (this.isBoundsAutoCompute()){
if (geometryInfo.getVertices().length >= 3){
this.setBounds(this.computeDefaultBounds());
}else{
// logger.error("Warning: could not compute bounds because too few vertices were supplied: " + this.getName() + " in " + this + " -> Setting boundingShape to null.");
this.setBounds(null);
}
}else{
this.setBounds(null);
}
//Sets the base matrix dirty, so that when inquiring info about
//vertices, they get updated first
// this.setLocalBaseMatrixDirty(true);
this.globalVerticesDirty = true;
}
/**
* Returns the vertices of this shape without any transformations applied
* <br> <b>Caution:</b> If you alter them in anyway, changes will only
* be consistent by calling the <code>setVertices(Vertex[])</code> method with the changes vertices
* as an argument!.
*
* @return the untransformed vertices
*/
public Vertex[] getVerticesLocal(){
return this.getGeometryInfo().getVertices();
}
/**
* Returns the vertices of this shape in real world (global) coordinates
* <br> <b>Caution:</b> If you alter them in anyway, changes will only
* be consistent if you call the setVertices() method of the shape.
* <br><b>Caution:</b>This operation is not cheap since all vertices are
* first copied and then transformed!
* <br><b>Note:</b> if a shape as a lot of vertices this will increase memory usage considerably
* because a complete copy of the shapes vertices is made and kept!
* @return the vertices in global coordinate space
*/
public Vertex[] getVerticesGlobal(){
this.updateVerticesGlobal();
return this.verticesGlobal;
}
/**
* Updates the verticesglobal array of the shape by
* multipying them with the current shape's global matrix.<br>
* <br>This calculates the real world space coordinates and saves it
* in the verticesglobal array. These vertices can be used to test at picking
* or just to know the real world global coordinates of the vertices.
*/
private void updateVerticesGlobal(){
if (this.globalVerticesDirty){
Vertex[] unTransformedCopy = Vertex.getDeepVertexArrayCopy(this.getGeometryInfo().getVertices());
//transform the copied vertices and save them in the vertices array
this.verticesGlobal = Vertex.transFormArray(this.getGlobalMatrix(), unTransformedCopy);
this.globalVerticesDirty = false;
}
}
/**
* Gets the vertex count.
*
* @return the vertex count
*
* the number of vertices for that shape
*/
public int getVertexCount(){
return this.getGeometryInfo().getVertexCount();
}
/**
* Checks if is use direct gl.
*
* @return true, if checks if is use direct gl
*
* true, if the shape tries to draw itself with OpenGL commands
* rather than processing commands
*/
public boolean isUseDirectGL() {
return this.drawDirectGL;
}
/**
* If set to true - which is the default if using the OpenGL render mode -
* this shape will bypass processings rendering pipeline
* and use the OpenGL context directly for performance increases.<br>
* Setting this to false forces the use of the processing renderer.
* <p>
* If this is set to true, and additionally, setUseVBOs() is set to true,
* the shape is drawn by using vertex buffer objects (VBO). <br>
* By calling setUseDisplayList(true) it is drawn using display lists.
*
* @param drawPureGL the draw pure gl
*/
public void setUseDirectGL(boolean drawPureGL){
if (MT4jSettings.getInstance().isOpenGlMode()){
if (
!this.isUseDirectGL() &&
drawPureGL //FIXME WHY WAS THIS MISSING? is there a reason not to put this condition?
&& this.getGeometryInfo().getVertices() != null
&& this.getGeometryInfo().getVertexCount() > 0){
//Generate buffers for opengl array use
this.getGeometryInfo().generateOrUpdateBuffersLocal(this.getStyleInfo());
}
this.drawDirectGL = drawPureGL;
//Wrap the current texture into a gl texture object for openGl use
if (this.drawDirectGL
&& this.getTexture() != null
// && !(this.getTexture() instanceof MTTexture) //dont do so that tex coords get updated if previously set GLTexture was set with no directGL mode
){
this.setTexture(this.getTexture());
}
}else{
logger.error(this.getName() + " - Cant use direct GL mode if not in opengl mode! Object: " + this.getName());
this.drawDirectGL = false;
}
}
/**
* Checks if this shape is drawn using VBOs.
*
* @return true, if checks if is use vbos
*
* true, if the shape tries to draw itself with OpenGL Vertex Buffer Objects
*/
public boolean isUseVBOs() {
return this.useVBOs;
}
/**
* <br>Tries to use Vertex Buffer Objects for displaying this shape.<br>
* You have to be in OpenGL mode and set <code>setDrawDirectGL(true)</code>first.
*
* @param useVBOs the use vb os
*/
public void setUseVBOs(boolean useVBOs) {
if (MT4jSettings.getInstance().isOpenGlMode() && this.isUseDirectGL()){
if (!this.isUseVBOs()){
this.getGeometryInfo().generateOrUpdateAllVBOs();
}
this.useVBOs = useVBOs;
}else{
logger.error(this.getName() + " - Cant use VBOs if not in opengl mode and setDrawDirectGL has to be set to true! Object: " + this.getName());
this.useVBOs = false;
}
}
/**
* Checks if is use display list.
*
* @return true, if checks if is use display list
*
* true, if the shape tries to draw itself with OpenGL display lists
*/
public boolean isUseDisplayList() {
return this.useDisplayList;
}
/**
* Tries to use a opengl display list for rendering this shape.<br>
* You have to be in OpenGL mode and <code>setDrawDirectGL()</code> has to
* be set to "true" first!
* <br><strong>NOTE: </strong> the display list has to be created first
* to use it! This can be done by calling <code>generateDisplayLists</code>.
* Instead of these 2 steps we can also just call <code>generateAndUseDisplayLists()</code>
* <br><strong>NOTE: </strong> if the shape was using a display list before we should delete it before setting
* a new one!
*
* @param useDisplayList the use display list
*/
public void setUseDisplayList(boolean useDisplayList) {
if (MT4jSettings.getInstance().isOpenGlMode() && this.isUseDirectGL()){
this.useDisplayList = useDisplayList;
if (this.getGeometryInfo().getDisplayListIDs()[0] == -1
&& this.getGeometryInfo().getDisplayListIDs()[1] == -1 ){
logger.warn(this.getName() + " - Warning, no displaylists created yet on component: " + this.getName());
}
}else{
if (useDisplayList)
logger.warn(this.getName() + " - Cant set display lists if not in opengl mode and setDrawDirectGL has to be set to true! Object: " + this.getName());
this.useDisplayList = false;
}
}
/**
* Generates 2 openGL display lists for drawing this shape.
* <br>One for the interior (with textures etc.) and
* one for drawing the outline.
* <br><code>setUseDirectGL</code> has to be set to true first!
* <br>To use the display lists for drawing, call <code>setUseDisplayList()</code>
* This method only generates them!
* <br><strong>NOTE: </strong> if the shape was using a display list before we should delete it before setting
* a new one!
*/
public void generateDisplayLists(){
if (MT4jSettings.getInstance().isOpenGlMode() && this.isUseDirectGL()){
this.getGeometryInfo().generateDisplayLists(
this.isTextureEnabled(),
this.getTexture(),
this.getFillDrawMode(),
this.isDrawSmooth(),
this.getStrokeWeight());
}else{
logger.error(this.getName() + " - Cannot create displaylist if not in openGL mode or if setUseDirectGL() hasnt been set to true!");
}
}
/**
* Generates and uses openGL display lists for drawing this
* shape.
*/
public void generateAndUseDisplayLists(){
this.generateDisplayLists();
this.setUseDisplayList(true);
}
/**
* Deletes the displaylists of the object and sets
* setUseDisplayList() to false.
*/
public void disableAndDeleteDisplayLists(){
this.getGeometryInfo().deleteDisplayLists();
this.setUseDisplayList(false);
}
//FIXME move to TOols3D!?
/**
* Calculates the 2D XY convex hull for this shape.
*
* @return the convex hull xy global
*/
public Vector3D[] getConvexHullXYGlobal(){
ArrayList<Vector3D> vers = new ArrayList<Vector3D>();
Vertex[] transVerts = this.getVerticesGlobal();
for (int i = 0; i < transVerts.length; i++) {
Vertex vertex = transVerts[i];
vers.add(vertex);
}
ArrayList<Vector3D> edgeList = ConvexQuickHull2D.getConvexHull2D(vers);
return (edgeList.toArray(new Vertex[edgeList.size()]));
}
@Override
public void setFillColor(MTColor color) {
super.setFillColor(color);
this.getGeometryInfo().setVerticesColorAll(color.getR(), color.getG(), color.getB(), color.getAlpha());
}
@Override
public void setStrokeColor(MTColor strokeColor) {
super.setStrokeColor(strokeColor);
if (MT4jSettings.getInstance().isOpenGlMode() && this.isUseDirectGL())
this.getGeometryInfo().setStrokeColorAll(strokeColor.getR(), strokeColor.getG(), strokeColor.getB(), strokeColor.getAlpha());
}
/**
* Tells the shape to use its texture.
* A texture has to be set previously!
*
* @param texture the texture
*/
public void setTextureEnabled(boolean texture){
this.textureEnabled = texture;
}
/**
* Checks if is texture enabled.
*
* @return true, if checks if is texture enabled
*
* true, if the shape is to use a texture
*/
public boolean isTextureEnabled(){
return this.textureEnabled;
}
/**
* Sets a texture for this shape.
* <br>Uses the texture coordinates in the provided vertices for drawing.
* <br>If openGL mode is used, it also creates a GLTexture object.
* <br>For best compatibility, power of two texture dimensions should be provided.
* If the provided texture is non power of two and you are in opengl mode, we try
* to use the RECTANGULAR texture extension.
* <br>If textures were disabled for this component, they are being enabled again.
*
* @param newTexImage the new tex image
*/
public void setTexture(PImage newTexImage){
if (newTexImage == null){
this.textureImage = null;
this.setTextureEnabled(false);
// System.out.println("Set texture to null");
return;
}
//TODO AbstractShape.updateGLTextureCoordinates(); ?
//-> updateTextureBuffer still needed if custom tex coords wanted
//-> use before setTexture()!
//TODO delete old GLTexture object??? may cause memory leak if not!
//But how to know if its not needed elsewhere? register in GLTexture object when its used and not?
//TODO make sure that NORMALIZED texture coords are supplied and BEFORE setting the texture!
//TODO Note that if we want to change the tex coords mannually, do it normalized, then for precaution update the buffer and then set the texture
//if the tex coords have to be un/normalized the updating is done twice but else we might miss updating it when we update from POT to POT..
//maybe make method updateTextureCoords() - maybe also call setTexture to un/normalize()
if (!this.isTextureEnabled())
this.setTextureEnabled(true);
if (lastTextureDimension.equalsVector(Vector3D.ZERO_VECTOR)){
lastTextureDimension.setXYZ(newTexImage.width, newTexImage.height, 0);
}
// if (MT4jSettings.getInstance().isOpenGlMode()){ //FIXME USE WHICH ONE?
if (this.isUseDirectGL()){
if (newTexImage instanceof GLTexture) {
GLTexture glTex = (GLTexture) newTexImage;
if (glTex.getTextureTargetEnum() == TEXTURE_TARGET.RECTANGULAR){
this.setTextureMode(PConstants.IMAGE);
if (this.getGeometryInfo().isTextureCoordsNormalized()){
//0..1 -> 0..width
this.unNormalizeFromPOTtoRectMode(newTexImage, this.getVerticesLocal());
this.getGeometryInfo().setTextureCoordsNormalized(false);
}else{
//0..oldWidth -> 0..newWidth GLTexture is NPOT but this component's texture coords have seemingly already been un-normalized
//FIXME dont do it if it has the same dimensions!
this.fromRectModeToRectMode(newTexImage, this.getVerticesLocal(), this.lastTextureDimension.x, this.lastTextureDimension.y);
}
}else{
//GLTexture is POT -> normalize tex coords if neccessary
this.setTextureMode(PConstants.NORMALIZED);
if (this.getGeometryInfo().isTextureCoordsNormalized()){
//0..1 -> 0..1
}else{
//0..width -> 0..1
this.normalizeFromRectMode(newTexImage, this.getVerticesLocal(), this.lastTextureDimension.x, this.lastTextureDimension.y);
this.getGeometryInfo().setTextureCoordsNormalized(true);
}
}
this.textureImage = newTexImage;
//FIXME always save last tex dimensions? also if POT?
this.lastTextureDimension.setXYZ(newTexImage.width, newTexImage.height, 0);
}else{
//We are in OpenGL mode but the new texture is not a GLTexture -> create new GLTexture from PImage
boolean isPOT = Tools3D.isPowerOfTwoDimension(newTexImage);
GLTextureSettings ts = new GLTextureSettings();
if (!isPOT){
ts.target = TEXTURE_TARGET.RECTANGULAR;
this.setTextureMode(PConstants.IMAGE);
if (this.getGeometryInfo().isTextureCoordsNormalized()){
//0..1 -> 0..newWidth
this.unNormalizeFromPOTtoRectMode(newTexImage, this.getVerticesLocal());
this.getGeometryInfo().setTextureCoordsNormalized(false);
}else{
//0..oldWidth -> 0..newWidth
//FIXME dont do it if it has the same dimensions!
this.fromRectModeToRectMode(newTexImage, this.getVerticesLocal(), this.lastTextureDimension.x, this.lastTextureDimension.y);
}
this.textureImage = newTexImage;
this.lastTextureDimension.setXYZ(newTexImage.width, newTexImage.height, 0);
}else{
ts.target = TEXTURE_TARGET.TEXTURE_2D;
this.setTextureMode(PConstants.NORMALIZED);
//We are in OpenGL mode, new texture is a PImage, is POT -> create POT GLTexture and un-normalize tex coords if neccessary
if (this.getGeometryInfo().isTextureCoordsNormalized()){
//0..1 -> 0..1
}else{
//normalize 0..width -> 0..1
this.normalizeFromRectMode(newTexImage, this.getVerticesLocal(), this.lastTextureDimension.x, this.lastTextureDimension.y);
this.getGeometryInfo().setTextureCoordsNormalized(true);
}
}
//Create new GLTexture from PImage
ts.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
ts.expansionFilter = EXPANSION_FILTER.Bilinear;
ts.wrappingHorizontal = WRAP_MODE.CLAMP_TO_EDGE;
ts.wrappingVertical = WRAP_MODE.CLAMP_TO_EDGE;
GLTexture newGLTexture = new GLTexture(this.getRenderer(), newTexImage, ts);
// newGLTexture.format = newTexImage.format; //FIXME REMOVE?
this.textureImage = newGLTexture;
this.lastTextureDimension.setXYZ(newTexImage.width, newTexImage.height, 0);
}
}else{
//We dont use OpenGL -> just set the PImage texture
this.textureImage = newTexImage;
this.lastTextureDimension.setXYZ(newTexImage.width, newTexImage.height, 0);
}
}
//TODO save the set texture dimensions so we can always scale from one NPOT texture to another NPOT texture coords
//(even if texture was set to null in between)
private Vector3D lastTextureDimension = new Vector3D();
private void unNormalizeFromPOTtoRectMode(PImage newTexture, Vertex[] verts){
for (int i = 0; i < verts.length; i++) {
Vertex vertex = verts[i];
vertex.setTexCoordU(vertex.getTexCoordU() * (float)newTexture.width);
vertex.setTexCoordV(vertex.getTexCoordV() * (float)newTexture.height);
// System.out.println("TexU:" + vertex.getTexCoordU() + " TexV:" + vertex.getTexCoordV()); //FIXME REMOVE
}
this.getGeometryInfo().updateTextureBuffer(this.isUseVBOs());
}
private void normalizeFromRectMode(PImage newTexture, Vertex[] verts, float oldTexWidth, float oldTexHeight){
for (int i = 0; i < verts.length; i++) {
Vertex vertex = verts[i];
// vertex.setTexCoordU(ToolsMath.map(vertex.getTexCoordU(), 0, oldTexWidth, 0, 1));
// vertex.setTexCoordV(ToolsMath.map(vertex.getTexCoordV(), 0, oldTexWidth, 0, 1));
vertex.setTexCoordU(vertex.getTexCoordU() / oldTexWidth);
vertex.setTexCoordV(vertex.getTexCoordV() / oldTexHeight);
}
this.getGeometryInfo().updateTextureBuffer(this.isUseVBOs());
}
private void fromRectModeToRectMode(PImage newTexture, Vertex[] verts, float oldTexWidth, float oldTexHeight){
for (int i = 0; i < verts.length; i++) {
Vertex vertex = verts[i];
vertex.setTexCoordU( (vertex.getTexCoordU() / oldTexWidth) * (float)newTexture.width);
vertex.setTexCoordV( (vertex.getTexCoordV() / oldTexHeight) * (float)newTexture.height);
}
this.getGeometryInfo().updateTextureBuffer(this.isUseVBOs());
}
/**
* Gets the texture.
*
* @return the texture
*
* the texture object associated with this shape (either a PImage or GLTexture obj)
*/
public PImage getTexture() {
return this.textureImage;
}
/**
* Sets the way texture coordinates are handled in processing. This setting
* is not considered if using OpenGL mode!
* Allowed values are: <code>PApplet.NORMALIZED</code> and <code>PApplet.IMAGE</code>
* <br>Default is <code>PApplet.NORMALIZED</code>.
* Which indicates that the texture coordinates should be in normalized
* range from 0.0 to 1.0!
* In image mode they have to range from 0..imageDimensions.
*
* @param textureMode the texture mode
*/
public void setTextureMode(int textureMode){
this.textureMode = textureMode;
}
/**
* Gets the processing texture mode.
*
* @return the texture mode
*/
public int getTextureMode(){
return this.textureMode;
}
/**
* Sets the global position of the component. (In global coordinates)
*
* @param pos the pos
*/
public void setPositionGlobal(Vector3D pos){
this.translateGlobal(pos.getSubtracted(this.getCenterPointGlobal()));
}
/**
* Sets the position of the component, relative to its parent coordinate frame.
*
* @param pos the pos
*/
public void setPositionRelativeToParent(Vector3D pos){
this.translate(pos.getSubtracted(this.getCenterPointRelativeToParent()), TransformSpace.RELATIVE_TO_PARENT);
}
/**
* Sets the position of this component, relative to the other specified component.
*
* @param otherComp the other comp
* @param pos the pos
*/
public void setPositionRelativeToOther(MTComponent otherComp, Vector3D pos){
Matrix m0 = MTComponent.getTransformToDestinationLocalSpace(otherComp, this);
pos.transform(m0);
Vector3D centerpointGlobal = this.getCenterPointGlobal();
centerpointGlobal.transform(this.getGlobalInverseMatrix()); //to localobj space
centerpointGlobal.transform(this.getLocalMatrix()); //to parent relative space
Vector3D diff = pos.getSubtracted(centerpointGlobal);
this.translate(diff, TransformSpace.RELATIVE_TO_PARENT);
// Vector3D globalCenter = this.getCenterPointGlobal();
//
// Vector3D localObjCenter = this.getCenterPointGlobal();
// localObjCenter.transform(this.getAbsoluteWorldToLocalMatrix()); //to localobj space
//// localObjCenter.transform(this.getLocalBasisMatrix()); //to parent relative space
//
//
//// pos = MTBaseComponent.getWorldVecToParentRelativeSpace(otherComp, pos);
//// pos.transform(otherComp.getLocalBasisMatrix());
//// pos = MTBaseComponent.getWorldVecToLocalRelativeSpace(otherComp, pos);
//
//// pos.transform(otherComp.getAbsoluteLocalToWorldMatrix());
//
// Matrix m = MTBaseComponent.getTransformToDestinationLocalSpace(this, otherComp);
//// m.multLocal(otherComp.getLocalBasisMatrix());
// Matrix m2 = MTBaseComponent.getTransformToDestinationParentSpace(this, otherComp);
//// pos.transform(m);
//
// /*
//
//// localObjCenter.transform(m2);
//// System.out.println("Localobjcenter transformed to other: " + localObjCenter);
//
// Vector3D diff = pos.minus(localObjCenter);
// diff.transformDirectionVector(m2);
// this.translateGlobal(diff);
// */
//
//
//// pos.transform(otherComp.getAbsoluteLocalToWorldMatrix());
//// localObjCenter.transform(this.getAbsoluteLocalToWorldMatrix());
//// localObjCenter.transform(otherComp.getAbsoluteWorldToLocalMatrix());
//// Vector3D diff = pos.minus(localObjCenter);
//
// Vector3D centerPoint = this.getBounds().getCenterPointLocal();
//// centerPoint.transform(this.getLocalBasisMatrix());
//
// //Muss punkt nach svgComp space transformieren, da diese comp gescaled wird
// centerPoint.transform(m);
// pos.transform(otherComp.getAbsoluteLocalToWorldMatrix());
// Vector3D diff = pos.minus(centerPoint);
//
// System.out.println("Point from where to start: " + localObjCenter);
// System.out.println("Destination pos: " + pos);
// System.out.println("Translation vector: " + diff);
//// Vector3D diff = pos.minus(localObjCenter);
//// diff.transformDirectionVector(m);
//
// this.translate(diff, TransformSpace.RELATIVE_TO_PARENT);
}
/* (non-Javadoc)
* @see org.mt4j.components.visibleComponents.AbstractVisibleComponent#getIntersectionLocal(org.mt4j.util.math.Ray)
*/
@Override
public Vector3D getIntersectionLocal(Ray ray) {
//TODO be aware that we dont call the super implementation here..
switch (this.getBoundsBehaviour()) {
case AbstractShape.BOUNDS_DONT_USE:
// System.out.println("\"" + this.getName() + "\": -> GEOMETRY only check");
return this.getGeometryIntersectionLocal(ray);
case AbstractShape.BOUNDS_ONLY_CHECK:
if (this.hasBounds()){
// System.out.println("\"" + this.getName() + "\": -> BOUNDS only check");
return this.getBounds().getIntersectionLocal(ray);
}else{
// System.out.println("\"" + this.getName() + "\": -> GEOMETRY only check");
return this.getGeometryIntersectionLocal(ray);
}
case AbstractShape.BOUNDS_CHECK_THEN_GEOMETRY_CHECK:
if (this.hasBounds()){
// System.out.println("\"" + this.getName() + "\": -> BOUNDS check then GEOMETRY check");
Vector3D boundsIntersection = this.getBounds().getIntersectionLocal(ray);
if (boundsIntersection != null){
return this.getGeometryIntersectionLocal(ray);
}else{
return null;
}
}else{
return this.getGeometryIntersectionLocal(ray);
}
default:
break;
}
return null;
}
@Override
protected boolean componentContainsPointLocal(Vector3D testPoint) {
switch (this.getBoundsBehaviour()) {
case AbstractShape.BOUNDS_DONT_USE:
// System.out.println("\"" + this.getName() + "\": -> GEOMETRY only check");
return this.isGeometryContainsPointLocal(testPoint);
case AbstractShape.BOUNDS_ONLY_CHECK:
if (this.hasBounds()){
// System.out.println("\"" + this.getName() + "\": -> BOUNDS only check");
return this.getBounds().containsPointLocal(testPoint);
}else{
// System.out.println("\"" + this.getName() + "\": -> GEOMETRY only check");
return this.isGeometryContainsPointLocal(testPoint);
}
case AbstractShape.BOUNDS_CHECK_THEN_GEOMETRY_CHECK:
if (this.hasBounds()){
// System.out.println("\"" + this.getName() + "\": -> BOUNDS check then GEOMETRY check");
if (this.getBounds().containsPointLocal(testPoint)){
return this.isGeometryContainsPointLocal(testPoint);
}else{
return false;
}
}else{
return this.isGeometryContainsPointLocal(testPoint);
}
default:
break;
}
return false;
}
/**
* Tests if the ray intersects the shape and where.
* The ray is assumed to be transformed to local space already!
*
* @param ray the ray
*
* @return the geometry intersection
*
* the intersection point or null if no intersection occured
*/
abstract public Vector3D getGeometryIntersectionLocal(Ray ray);
/**
* Tests is the geometry of the shape contains the given point.
* The testpoint is assumed to be transformed to local space already!
*
* @param testPoint the test point
*
* @return true, if checks if is geometry contains point
*/
abstract public boolean isGeometryContainsPointLocal(Vector3D testPoint);
/**
* Gets the center point global.
* First it gets the local center and then transforms it to the global frame.
*
* @return the center point global
*
* the center of this shape in global coordinates
*/
public final Vector3D getCenterPointGlobal(){
Vector3D center = this.getCenterPointLocal();
center.transform(this.getGlobalMatrix());
return center;
}
/**
* Gets the center point relative to parent.
* First it gets the local center and then transforms it to the parent frame.
* @return the center of this shape in coordinates relative to the shapes parent coordiante frame.
*/
public final Vector3D getCenterPointRelativeToParent(){
Vector3D center = this.getCenterPointLocal();
center.transform(this.getLocalMatrix());
return center;
}
/**
* Gets the center point in local object space.
* This should always return a COPY of the centerpoint of the implementing shape
* since the point may get transformed afterwards.
* @return the center point of this shape in untransformed local object coordinates.
*/
abstract public Vector3D getCenterPointLocal();
//
// /**
// * Get the width of the shape in the XY-Plane. Uses the x and y coordinate
// * values for calculation. Usually the calculation is delegated to the shapes
// * bounding shape.
// *
// * @param transformSpace the space the width is calculated in, can be global space, parent relative- or object space
// *
// * @return the width xy
// *
// * the width
// */
// abstract public float getWidthXY(TransformSpace transformSpace);
//
// /**
// * Get the height of the shape in the XY-Plane. Uses the x and y coordinate
// * values for calculation. Usually the calculation is delegated to the shapes
// * bounding shape.
// *
// * @param transformSpace the space the width is calculated in, can be world space, parent relative- or object space
// *
// * @return the height xy
// *
// * the height
// */
// abstract public float getHeightXY(TransformSpace transformSpace);
//
/**
* Get the height of the shape in the XY-Plane. Uses the x and y coordinate
* values for calculation. Usually the calculation is delegated to the shapes
* bounding shape.
*
* @param transformSpace the space the width is calculated in, can be world space, parent relative- or object space
*
* @return the height xy
*
* the height
*/
public float getHeightXY(TransformSpace transformSpace) {
switch (transformSpace) {
case LOCAL:
return this.getHeightXYLocal();
case RELATIVE_TO_PARENT:
return this.getHeightXYRelativeToParent();
case GLOBAL:
return this.getHeightXYGlobal();
default:
return -1;
}
}
/**
* Gets the height xy obj space.
* @return the height xy obj space
*/
private float getHeightXYLocal() {
return this.getHeightXYVectLocal().length();
}
/**
* Gets the "height vector" and transforms it to parent relative space, then calculates
* its length.
*
* @return the height xy relative to parent
*
* the height relative to its parent space frame
*/
protected float getHeightXYRelativeToParent() {
if (this.hasBounds()){
return this.getBounds().getHeightXY(TransformSpace.RELATIVE_TO_PARENT);
}else{
OrientedBoundingBox tempBounds = new OrientedBoundingBox(this);
return tempBounds.getHeightXY(TransformSpace.RELATIVE_TO_PARENT);
}
}
/**
* Gets the "height vector" and transforms it to world space, then calculates
* its length.
*
* @return the height xy global
*
* the height relative to the world space
*/
protected float getHeightXYGlobal() {
if (this.hasBounds()){
return this.getBounds().getHeightXY(TransformSpace.GLOBAL);
}else{
OrientedBoundingBox tempBounds = new OrientedBoundingBox(this);
return tempBounds.getHeightXY(TransformSpace.GLOBAL);
}
}
/**
* Gets the "height vector" from its boundingshape. If no boundingshape is set,
* a temporary bounding rectangle in the xy-plane is calculated and its height
* is calculated as a vector with the height as its length in object space.
*
* @return the height xy vect obj space
*
* vector representing the height of the boundingshape of the shape
* @deprecated this method should actually be private. Use getHeightXY(Transformspace.LOCAL) instead!
*/
public Vector3D getHeightXYVectLocal() {
if (this.hasBounds()){
return this.getBounds().getHeightXYVectLocal();
}else{
OrientedBoundingBox tempBounds = new OrientedBoundingBox(this);
return tempBounds.getHeightXYVectLocal();
}
}
/**
* Get the width of the shape in the XY-Plane. Uses the x and y coordinate
* values for calculation. Usually the calculation is delegated to the shapes
* bounding shape.
*
* @param transformSpace the space the width is calculated in, can be global space, parent relative- or object space
*
* @return the width xy
*
* the width
*/
public float getWidthXY(TransformSpace transformSpace) {
switch (transformSpace) {
case LOCAL:
return this.getWidthXYLocal();
case RELATIVE_TO_PARENT:
return this.getWidthXYRelativeToParent();
case GLOBAL:
return this.getWidthXYGlobal();
default:
return -1;
}
}
/**
* Gets the width xy obj space.
*
* @return the width xy obj space
*/
private float getWidthXYLocal() {
return this.getWidthXYVectLocal().length();
}
/**
* Calculates the width of this shape, by using its
* bounding shape.
* Uses the objects local transform. So the width will be
* relative to the parent only - not the whole world
*
* @return the width xy relative to parent
*
* the width
*/
protected float getWidthXYRelativeToParent() {
if (this.hasBounds()){
return this.getBounds().getWidthXY(TransformSpace.RELATIVE_TO_PARENT);
}else{
OrientedBoundingBox tempBounds = new OrientedBoundingBox(this);
return tempBounds.getWidthXY(TransformSpace.RELATIVE_TO_PARENT);
}
}
/**
* Gets the "Width vector" and transforms it to world space, then calculates
* its length.
*
* @return the width xy global
*
* the Width relative to the world space
*/
protected float getWidthXYGlobal() {
if (this.hasBounds()){
return this.getBounds().getWidthXY(TransformSpace.GLOBAL);
}else{
OrientedBoundingBox tempBounds = new OrientedBoundingBox(this);
return tempBounds.getWidthXY(TransformSpace.GLOBAL);
}
}
/**
* Gets the "width vector" from its boundingshape. If no boundingshape is set,
* a temporary bounding rectangle in the xy-plane is calculated and its width
* is calculated as a vector with the width as its length in object space.
*
* @return the width xy vect obj space
*
* vector representing the width of the boundingshape of the shape
* @deprecated this method should actually be private. Use getWidthXY(Transformspace.LOCAL) instead!
*/
public Vector3D getWidthXYVectLocal() {
if (this.hasBounds()){
return this.getBounds().getWidthXYVectLocal();
}else{
OrientedBoundingBox tempBounds = new OrientedBoundingBox(this);
return tempBounds.getWidthXYVectLocal();
}
}
/**
* <li>Removes this component from its parent.
* <li>Calls <code>destroyComponent</code> on this component which
* can be used to free resources that the component used.
* <li>Recursively calls destroy() on its children
* <br>
* <p>
* By default, the openGl texture object and the VBOs associated with this shape will be deleted.
* Be careful when you share textures or VBOs across more than one object!
* Destroying of displaylists isnt done atm! Use disableAndDeleteDisplaylists() instead.
*/
@Override
public void destroy(){
// System.out.println(this + " -> DESTROY() -> (AbstractShape)");
//FIXME if vbos or display lists are shared, they shouldnt be deleted!
//right now, destroying of displaylists isnt done. Use disableAndDeleteDisplaylists() instead.
if (this.geometryInfo != null){
//Delete VBOs
this.getGeometryInfo().deleteAllVBOs();
}
this.destroyDisplayLists();
/*
//Delete displaylist
this.disableAndDeleteDisplayLists();
*/
// this.geometryInfo = null; //FIXME TEST
// this.boundingShape = null;
this.setBounds(null);
//Delete openGL texture object
if (this.getTexture() instanceof GLTexture){
GLTexture tex = (GLTexture) this.getTexture();
//Delete texture
tex.destroy();
this.setTexture(null);
this.setTextureEnabled(false);
}
super.destroy();
}
/**
* This is called during the shape's destroy() method.
* Override this and leave it empty if you dont want the
* display list destroyed in your component
* (makes sense with shared geometry infos/display lists)
*/
protected void destroyDisplayLists(){
// /*
//Delete displaylist
this.disableAndDeleteDisplayLists();
// */
}
@Override
abstract protected void destroyComponent();
/**
* Moves this shape to the specified global position using an animation specified
* by the last three parameters.
*
* @param x the x
* @param y the y
* @param z the z
* @param interpolationDuration the interpolation duration
* @param accelerationEndTime the acceleration end time - normalized value 0..1
* @param decelerationStartTime the deceleration start time - normalized value 0..1
* @return the animation
*/
public Animation tweenTranslateTo(float x, float y, float z, float interpolationDuration, float accelerationEndTime, float decelerationStartTime){
Vector3D from = this.getCenterPointGlobal();
Vector3D targetPoint = new Vector3D(x, y, z);
Vector3D directionVect = targetPoint.getSubtracted(from);
//GO through all animations for this shape
Animation[] animations = AnimationManager.getInstance().getAnimationsForTarget(this);
for (int i = 0; i < animations.length; i++) {
Animation animation = animations[i];
//Go through all listeners of these animations
IAnimationListener[] animationListeners = animation.getAnimationListeners();
for (int j = 0; j < animationListeners.length; j++) {
IAnimationListener listener = animationListeners[j];
//IF a listener is a TranslationAnimationListener the animations is a translationTween
//and should be stopped before doing this new animation
if (listener instanceof TranslationAnimationListener)
animation.stop();
}
}
return this.tweenTranslate(directionVect, interpolationDuration, accelerationEndTime, decelerationStartTime);
}
/**
* Moves this shape in the specified direction with an animation specified by the other parameters.
*
* @param directionVect the direction vect
* @param interpolationDuration the interpolation duration
* @param accelerationEndTime the acceleration end time - normalized value 0..1
* @param decelerationStartTime the deceleration start time - normalized value 0..1
* @return the animation
*/
public Animation tweenTranslate(Vector3D directionVect, float interpolationDuration, float accelerationEndTime, float decelerationStartTime){
return this.tweenTranslate(directionVect, interpolationDuration, accelerationEndTime, decelerationStartTime, 0);
}
/**
* Tween translate.
*
* @param directionVect the direction vect
* @param interpolationDuration the interpolation duration
* @param accelerationEndTime the acceleration end time - normalized value 0..1
* @param decelerationStartTime the deceleration start time - normalized value 0..1
* @param triggerDelay the trigger delay
* @return the animation
*/
public Animation tweenTranslate(Vector3D directionVect, float interpolationDuration, float accelerationEndTime, float decelerationStartTime, int triggerDelay){
float distance = directionVect.length();
MultiPurposeInterpolator interpolator = new MultiPurposeInterpolator(0, distance, interpolationDuration , accelerationEndTime, decelerationStartTime , 1);
Animation animation = new Animation("Tween translate of " + this.getName(), interpolator, this, triggerDelay);
animation.addAnimationListener(new TranslationAnimationListener(this, directionVect));
animation.setResetOnFinish(false);
animation.start();
return animation;
}
/**
* This private class acts as an AnimationListener for translation animations.
*
* @author C.Ruff
*/
private class TranslationAnimationListener implements IAnimationListener{
/** The direction vector. */
private Vector3D directionVector;
/** The normalized dir vect. */
private Vector3D normalizedDirVect;
/** The shape. */
private AbstractShape shape;
/**
* Instantiates a new translation animation listener.
*
* @param shape the shape
* @param directionVector the direction vector
*/
public TranslationAnimationListener(AbstractShape shape, Vector3D directionVector){
this.directionVector = directionVector;
this.normalizedDirVect = this.directionVector.getCopy();
this.normalizedDirVect.normalizeLocal();
this.shape = shape;
}
/* (non-Javadoc)
* @see util.animation.IAnimationListener#processAnimationEvent(util.animation.AnimationEvent)
*/
public void processAnimationEvent(AnimationEvent ae) {
Object target = ae.getTargetObject();
if (target != null && target.equals(this.shape)){
AbstractShape shape = (AbstractShape)target;
float amount = ae.getAnimation().getInterpolator().getCurrentStepDelta();
Vector3D newTranslationVect = this.normalizedDirVect.getCopy();
newTranslationVect.scaleLocal(amount);
//Move shape
// shape.translateGlobal(newTranslationVect);
shape.translate(newTranslationVect);
}
}
}
}