/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.scenegraph.primitives; import org.jagatoo.opengl.enums.TextureFormat; import org.openmali.FastMath; import org.openmali.spatial.bounds.BoundingSphere; import org.openmali.types.twodee.Dim2i; import org.openmali.types.twodee.Sized2iRO; import org.openmali.vecmath2.Matrix3f; import org.openmali.vecmath2.Point3f; import org.openmali.vecmath2.TexCoord2f; import org.openmali.vecmath2.Tuple2f; import org.openmali.vecmath2.Tuple3f; import org.openmali.vecmath2.Vector3f; import org.xith3d.scenegraph.Appearance; import org.xith3d.scenegraph.Billboard; import org.xith3d.scenegraph.Shape3D; import org.xith3d.scenegraph.Texture; import org.xith3d.scenegraph.TransparencyAttributes; import org.xith3d.scenegraph.TriangleStripArray; import org.xith3d.scenegraph.Geometry.Optimization; /** * A simple implementation for the Billboard interface. It does not inherit from * Rectangle or TextRectangle as RectBillboard and TextBillboard do to avoid * geometry duplication. * * Moreover, features like creating a text Billboard should go into a factory * class and not clutter this class with numerous constructors. * * @see FixedSizedBillboardFactory * * @author Herve */ public class FixedSizedBillboard extends Shape3D implements Billboard { private long lastFrameId = -Long.MAX_VALUE; private final float width; private final float height; private final Point3f center; private final Point3f[] vertices; private Dim2i sizeOnScreen; private final float defaultBoundingRadius; private final Vector3f radiusPoint = new Vector3f(); /** * Sets the texture used by this billboard. * * @param texture the texture to use, or {@code null} to remove the texture. * @param texLowerLeft the texture lower left coordinate. It can be * {@code null} in which case the (0f;0f) value is assumed. * @param texUpperRight the texture upper right coordinate. It can be * {@code null} in which case the (1f;1f) value is assumed. */ public void setTexture( Texture texture, Tuple2f texLowerLeft, Tuple2f texUpperRight ) { if ( texture != null ) { final Appearance appearance = getAppearance( true ); appearance.setTexture( texture ); if ( texture.getFormat() == TextureFormat.RGBA ) { final TransparencyAttributes transparency = appearance.getTransparencyAttributes( true ); transparency.setMode( TransparencyAttributes.BLENDED ); transparency.setTransparency( 0f ); appearance.setTransparencyAttributes( transparency ); } setTexturePosition( texLowerLeft, texUpperRight ); } else { Appearance appearance = getAppearance(); if ( appearance != null ) { appearance.setTexture( (Texture)null ); } } } /** * Sets the new texture-coordinates for this Rectangle. * * @param texLowerLeft the texture lower left coordinate. It can be * {@code null} in which case the (0f;0f) value is assumed. * @param texUpperRight the texture upper right coordinate. It can be * {@code null} in which case the (1f;1f) value is assumed. */ public void setTexturePosition( Tuple2f texLowerLeft, Tuple2f texUpperRight ) { TexCoord2f textureCoordinates[] = { new TexCoord2f( 0f, 0f ), new TexCoord2f( 1f, 0f ), new TexCoord2f( 0f, 1f ), new TexCoord2f( 1f, 1f ) }; if ( texLowerLeft != null ) { textureCoordinates[ 0 ].setS( texLowerLeft.getX() ); textureCoordinates[ 0 ].setT( texLowerLeft.getY() ); textureCoordinates[ 1 ].setT( texLowerLeft.getY() ); textureCoordinates[ 2 ].setS( texLowerLeft.getX() ); } if ( texUpperRight != null ) { textureCoordinates[ 1 ].setS( texUpperRight.getX() ); textureCoordinates[ 2 ].setT( texUpperRight.getY() ); textureCoordinates[ 3 ].setS( texUpperRight.getX() ); textureCoordinates[ 3 ].setT( texUpperRight.getY() ); } final TriangleStripArray geometry = this.getGeometry(); geometry.setTextureCoordinates( 0, 0, textureCoordinates ); } /** * Returns the desired size on screen for this billboard, in pixels. * @return the desired size on screen, or {@code null} for a classical * billboard. */ public Sized2iRO getSizeOnScreen() { if ( sizeOnScreen == null ) { return ( null ); } if ( ( sizeOnScreen.getWidth() <= 0 ) || ( sizeOnScreen.getHeight() <= 0 ) ) { return ( null ); } return ( sizeOnScreen ); } /** * Sets the desired size on screen in pixels. If any of the given values are * negative or zero, the {@link #getSizeOnScreen()} method will return * {@code null}. * @param width the desired width in pixels. * @param height the desired height in pixels. */ public void setSizeOnScreen( int width, int height ) { if ( sizeOnScreen == null ) { this.sizeOnScreen = new Dim2i(); } this.sizeOnScreen.set( width, height ); } /** * Interface implementation. */ public void updateFaceToCamera( final Matrix3f viewRotation, final long frameId, long nanoTime, long nanoStep ) { // Check frame number if ( frameId <= this.lastFrameId ) { return; } this.lastFrameId = frameId; // Update vertices values setDefaultVerticesValues(); radiusPoint.set( vertices[ 0 ].getX(), 0f, 0f ); for ( int i = 0; i < vertices.length; i++ ) { viewRotation.transform( vertices[ i ] ); } // Update bounds (needed for pickable constant-size-on-screen billboards) viewRotation.transform( radiusPoint ); ( (BoundingSphere)getBounds() ).setRadius( radiusPoint.length() ); setBoundsDirty(); // Update geometry this.getGeometry().setCoordinates( 0, this.vertices ); } @Override public TriangleStripArray getGeometry() { return ( (TriangleStripArray)super.getGeometry() ); } private static TriangleStripArray createGeometry( final Tuple3f[] coordinates ) { final TriangleStripArray geometry = new TriangleStripArray( 4 ); geometry.setCoordinates( 0, coordinates ); // IMPORTANT: if optimization is set to another value than NONE, then // the geometry is not updated to face the camera. geometry.setOptimization( Optimization.NONE ); return ( geometry ); } /** * Sets the Billboard vertices values to their default (non-rotated) values. * */ private void setDefaultVerticesValues() { final float x0 = center.getX(); final float y0 = center.getY(); final float z0 = center.getZ(); final float w0 = sizeOnScreen != null ? 0.5f : width / 2f; final float h0 = sizeOnScreen != null ? 0.5f : height / 2f; vertices[ 0 ].set( x0 - w0, y0 - h0, z0 ); vertices[ 1 ].set( x0 + w0, y0 - h0, z0 ); vertices[ 2 ].set( x0 - w0, y0 + h0, z0 ); vertices[ 3 ].set( x0 + w0, y0 + h0, z0 ); } private static Point3f[] createVertices() { return ( new Point3f[] { new Point3f(), new Point3f(), new Point3f(), new Point3f() } ); } /** * Constructor. * @param center the billboard center. Can be {@code null}, and will * usually be. * @param width the billboard width. * @param height the billboard height. */ public FixedSizedBillboard( Tuple3f center, float width, float height ) { this.center = ( center == null ? new Point3f() : new Point3f( center ) ); this.width = width; this.height = height; this.vertices = createVertices(); this.sizeOnScreen = null; this.defaultBoundingRadius = FastMath.sqrt( width * width + height * height ) / 2f; setDefaultVerticesValues(); setGeometry( createGeometry( this.vertices ) ); setBounds( new BoundingSphere( this.center, defaultBoundingRadius ) ); } }