/** * 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.particles.jops; import java.util.List; import org.jagatoo.opengl.OGL; import org.jagatoo.opengl.enums.BlendFunction; import org.jagatoo.opengl.enums.DrawMode; import org.jagatoo.opengl.enums.PerspectiveCorrectionMode; import org.jagatoo.opengl.enums.TestFunction; import org.jagatoo.opengl.enums.TextureBoundaryMode; import org.jagatoo.opengl.enums.TextureCombineMode; import org.jagatoo.opengl.enums.TextureMode; import org.openmali.FastMath; import org.openmali.types.twodee.Sized2iRO; import org.openmali.vecmath2.Colorf; import org.openmali.vecmath2.Matrix3f; import org.openmali.vecmath2.Point3f; import org.openmali.vecmath2.TexCoord2f; import org.openmali.vecmath2.Vector3f; import org.softmed.jops.Generator; import org.softmed.jops.GeneratorBehaviour; import org.softmed.jops.Particle; import org.xith3d.loaders.texture.TextureLoader; import org.xith3d.scenegraph.Appearance; import org.xith3d.scenegraph.Billboard; import org.xith3d.scenegraph.IndexedTriangleArray; import org.xith3d.scenegraph.PolygonAttributes; import org.xith3d.scenegraph.RenderingAttributes; import org.xith3d.scenegraph.Shape3D; import org.xith3d.scenegraph.Texture; import org.xith3d.scenegraph.TextureAttributes; import org.xith3d.scenegraph.TransparencyAttributes; import org.xith3d.scenegraph.Geometry.Optimization; /** * Insert comment here. * * @author Guilherme Gomes (aka guilhermegrg) */ public class GeneratorShape3D extends Shape3D implements Billboard { private static boolean WIRE_FRAME = false; private Generator generator; private int count; private Vector3f viewUp = new Vector3f(); private Vector3f viewRight = new Vector3f(); private TexCoord2f bl = new TexCoord2f(); private TexCoord2f br = new TexCoord2f(); private TexCoord2f tl = new TexCoord2f(); //private Vector3f side = new Vector3f(); //private Vector3f up = new Vector3f(); private Vector3f particleRight = new Vector3f(); private float correctedVAngle; private int[] index; private final float[] coordsBuffer = new float[ 9 ]; private final float[] colorBuffer = new float[ 4 ]; private Vector3f particleUp = new Vector3f(); private boolean relativeOrientation = false; private static final TextureAttributes texAttribs; private static final RenderingAttributes renderingAttribs; static { //texAttribs = new TextureAttributes( TextureMode.COMBINE_REPLACE, null, null, PerspectiveCorrectionMode.NICEST ); texAttribs = new TextureAttributes( TextureMode.MODULATE, null, null, PerspectiveCorrectionMode.NICEST ); texAttribs.setCombineAlphaMode( TextureCombineMode.REPLACE ); renderingAttribs = new RenderingAttributes( true, false, 0.001f, TestFunction.GREATER ); } public static final void setWireFrameEnabled( boolean mode ) { WIRE_FRAME = mode; } public static boolean isWireFrameEnabled() { return ( WIRE_FRAME ); } /** * {@inheritDoc} */ @Override public final IndexedTriangleArray getGeometry() { return ( (IndexedTriangleArray)super.getGeometry() ); } public void setGenerator( Generator generator ) { this.generator = generator; setup(); } public final Generator getGenerator() { return ( generator ); } protected void rotate2f( TexCoord2f v, float angle ) { v.set( v.getS() * FastMath.cos( angle ) - v.getT() * FastMath.sin( angle ), v.getT() * FastMath.cos( angle ) + v.getS() * FastMath.sin( angle ) ); } private void setTexture( int index, Particle particle ) { final float texWidth = 1f / particle.texWidth; final float texHeight = 1f / particle.texHeight; bl.set( -0.5f, -0.5f ); br.set( +1.5f, -0.5f ); tl.set( -0.5f, +1.5f ); rotate2f( bl, particle.angle ); rotate2f( br, particle.angle ); rotate2f( tl, particle.angle ); final float bias = 0.5f; bl.set( bl.getS() * texWidth + bias, bl.getT() * texHeight + bias ); br.set( br.getS() * texWidth + bias, br.getT() * texHeight + bias ); tl.set( tl.getS() * texWidth + bias, tl.getT() * texHeight + bias ); final IndexedTriangleArray geom = getGeometry(); geom.setTextureCoordinate( 0, index + 0, bl ); geom.setTextureCoordinate( 0, index + 1, br ); geom.setTextureCoordinate( 0, index + 2, tl ); } private void setColor( int index, Colorf color, float alpha ) { colorBuffer[0] = color.getRed(); colorBuffer[1] = color.getGreen(); colorBuffer[2] = color.getBlue(); colorBuffer[3] = alpha; final IndexedTriangleArray geom = getGeometry(); geom.setColor( index, colorBuffer ); geom.setColor( index + 1, colorBuffer ); geom.setColor( index + 2, colorBuffer ); } protected void buildFacingCameraTriangles( float[] carray, Particle particle ) { Vector3f position = new Vector3f( particle.position ); // provides rotation getWorldTransform().transform( position ); float tsize = particle.size; float width = particle.width * 0.25f; float height = particle.height * 0.25f; Point3f tup = new Point3f(); tup.set( viewUp.getX(), viewUp.getY(), viewUp.getZ() ); tup.scale( height ); Point3f tdir = new Point3f(); tdir.set( viewRight.getX(), viewRight.getY(), viewRight.getZ() ); tdir.scale( width ); Point3f mup = new Point3f( tup ); mup.scale( -0.5f ); Point3f mdir = new Point3f( tdir ); mdir.scale( -0.5f ); mup.add( mdir ); Point3f topleftT = new Point3f(); Point3f bottomleftT = new Point3f(); Point3f bottomrightT = new Point3f(); topleftT.setX( tup.getX() - tdir.getX() - mup.getX() ); topleftT.setY( tup.getY() - tdir.getY() - mup.getY() ); topleftT.setZ( tup.getZ() - tdir.getZ() - mup.getZ() ); bottomleftT.setX( -tup.getX() - tdir.getX() - mup.getX() ); bottomleftT.setY( -tup.getY() - tdir.getY() - mup.getY() ); bottomleftT.setZ( -tup.getZ() - tdir.getZ() - mup.getZ() ); bottomrightT.setX( -tup.getX() + tdir.getX() - mup.getX() ); bottomrightT.setY( -tup.getY() + tdir.getY() - mup.getY() ); bottomrightT.setZ( -tup.getZ() + tdir.getZ() - mup.getZ() ); bottomrightT.scale( tsize ); bottomleftT.scale( tsize ); topleftT.scale( tsize ); carray[0] = bottomleftT.getX() + position.getX(); carray[1] = bottomleftT.getY() + position.getY(); carray[2] = bottomleftT.getZ() + position.getZ(); carray[3] = bottomrightT.getX() + position.getX(); carray[4] = bottomrightT.getY() + position.getY(); carray[5] = bottomrightT.getZ() + position.getZ(); carray[6] = topleftT.getX() + position.getX(); carray[7] = topleftT.getY() + position.getY(); carray[8] = topleftT.getZ() + position.getZ(); } protected void buildAbsoluteOrientedTriangles( float[] carray, Particle particle/*, Vector3f up, Vector3f right*/ ) { correctedVAngle = particle.angleV - FastMath.PI_HALF; particleUp.setX( FastMath.cos( particle.angleH ) * FastMath.sin( correctedVAngle ) ); particleUp.setZ( FastMath.sin( particle.angleH ) * FastMath.sin( correctedVAngle ) ); particleUp.setY( FastMath.cos( correctedVAngle ) ); particleRight.setX( FastMath.cos( particle.angleH ) * FastMath.sin( particle.angleV ) ); particleRight.setZ( FastMath.sin( particle.angleH ) * FastMath.sin( particle.angleV ) ); particleRight.setY( FastMath.cos( particle.angleV ) ); Vector3f side = new Vector3f(); side.cross( particleRight, particleUp ); Vector3f position = new Vector3f( particle.position ); // provides rotation getWorldTransform().getMatrix4f().transform( position ); final float tsize = particle.size * 2f; final float width = particle.width * 0.25f; final float height = particle.height * 0.25f; Point3f tup = new Point3f(); tup.set( particleUp ); tup.scale( height ); Point3f tdir = new Point3f(); tdir.set( side ); tdir.scale( width ); Point3f mup = new Point3f( tup ); mup.scale( -0.5f ); Point3f mdir = new Point3f( tdir ); mdir.scale( -0.5f ); mup.add( mdir ); Point3f topLeftT = new Point3f(); Point3f bottomLeftT = new Point3f(); Point3f bottomRightT = new Point3f(); topLeftT.setX( tup.getX() - tdir.getX() - mup.getX() ); topLeftT.setY( tup.getY() - tdir.getY() - mup.getY() ); topLeftT.setZ( tup.getZ() - tdir.getZ() - mup.getZ() ); bottomLeftT.setX( -tup.getX() - tdir.getX() - mup.getX() ); bottomLeftT.setY( -tup.getY() - tdir.getY() - mup.getY() ); bottomLeftT.setZ( -tup.getZ() - tdir.getZ() - mup.getZ() ); bottomRightT.setX( -tup.getX() + tdir.getX() - mup.getX() ); bottomRightT.setY( -tup.getY() + tdir.getY() - mup.getY() ); bottomRightT.setZ( -tup.getZ() + tdir.getZ() - mup.getZ() ); bottomRightT.scale( tsize ); bottomLeftT.scale( tsize ); topLeftT.scale( tsize ); if ( particle.rotation != null ) { particle.rotation.transform( bottomRightT ); particle.rotation.transform( bottomLeftT ); particle.rotation.transform( topLeftT ); } if ( relativeOrientation ) { getWorldTransform().getMatrix4f().transform( bottomRightT ); getWorldTransform().getMatrix4f().transform( bottomLeftT ); getWorldTransform().getMatrix4f().transform( topLeftT ); } carray[0] = bottomLeftT.getX() + position.getX(); carray[1] = bottomLeftT.getY() + position.getY(); carray[2] = bottomLeftT.getZ() + position.getZ(); carray[3] = bottomRightT.getX() + position.getX(); carray[4] = bottomRightT.getY() + position.getY(); carray[5] = bottomRightT.getZ() + position.getZ(); carray[6] = topLeftT.getX() + position.getX(); carray[7] = topLeftT.getY() + position.getY(); carray[8] = topLeftT.getZ() + position.getZ(); } private void buildTriangles( float[] carray, Particle particle/*, Vector3f up, Vector3f right*/ ) { if ( generator.isAbsoluteParticleAngle() ) { buildAbsoluteOrientedTriangles( carray, particle/*, up, right*/ ); } else { buildFacingCameraTriangles( carray, particle ); } } public void update( Vector3f up, Vector3f right ) { this.viewUp.set( up ); this.viewRight.set( right ); } protected void update() { count++; List<Particle> particles = generator.getParticles(); Particle particle = null; int aliveParticles = 0; int tempVertexCount = 0; if ( GeneratorShape3D.isWireFrameEnabled() ) { for ( int i = 0; i < particles.size(); i++ ) { particle = particles.get( i ); if ( particle.life <= 0f ) { continue; } tempVertexCount = aliveParticles * 3; index[tempVertexCount + 0] = tempVertexCount + 0; index[tempVertexCount + 1] = tempVertexCount + 1; index[tempVertexCount + 2] = tempVertexCount + 2; buildTriangles( coordsBuffer, particle/*, viewUp, viewRight*/ ); setColor( i * 3, particle.color, particle.alpha ); getGeometry().setCoordinates( i * 3, coordsBuffer ); aliveParticles++; } } else { for ( int i = 0; i < particles.size(); i++ ) { particle = particles.get( i ); if ( particle.life <= 0f ) { continue; } tempVertexCount = aliveParticles * 3; index[tempVertexCount + 0] = tempVertexCount + 0; index[tempVertexCount + 1] = tempVertexCount + 1; index[tempVertexCount + 2] = tempVertexCount + 2; buildTriangles( coordsBuffer, particle/*, viewUp, viewRight*/ ); setColor( i * 3, particle.color, particle.alpha ); getGeometry().setCoordinates( i * 3, coordsBuffer ); setTexture( i * 3, particle ); aliveParticles++; } } getGeometry().setIndex( index ); getGeometry().setInitialIndexIndex( 0 ); getGeometry().setValidIndexCount( tempVertexCount ); } public void updateFaceToCamera( Matrix3f viewRotation, long frameId, long nanoTime, long nanoStep ) { update(); } protected BlendFunction getBlendFactor( int glBlendFactorInteger ) { //return ( BlendFunction.ONE ); switch ( glBlendFactorInteger ) { case OGL.GL_ZERO: return ( BlendFunction.ZERO ); case OGL.GL_ONE: return ( BlendFunction.ONE ); case OGL.GL_DST_ALPHA: return ( BlendFunction.DST_ALPHA ); case OGL.GL_DST_COLOR: return ( BlendFunction.DST_COLOR ); case OGL.GL_SRC_ALPHA: return ( BlendFunction.SRC_ALPHA ); case OGL.GL_SRC_COLOR: return ( BlendFunction.SRC_COLOR ); case OGL.GL_ONE_MINUS_DST_ALPHA: return ( BlendFunction.ONE_MINUS_DST_ALPHA ); case OGL.GL_ONE_MINUS_DST_COLOR: return ( BlendFunction.ONE_MINUS_DST_COLOR ); case OGL.GL_ONE_MINUS_SRC_ALPHA: return ( BlendFunction.ONE_MINUS_SRC_ALPHA ); case OGL.GL_ONE_MINUS_SRC_COLOR: return ( BlendFunction.ONE_MINUS_SRC_COLOR ); case OGL.GL_SRC_ALPHA_SATURATE: return ( BlendFunction.SRC_ALPHA_SATURATE ); default: throw new RuntimeException( "GL blend mode not recognized : " + glBlendFactorInteger ); } } private void setup() { if ( generator.getGb() == null || generator.getRender() == null ) return; GeneratorBehaviour gb = generator.getGb(); //final int vertexFormat = GeometryArray.COORDINATES | GeometryArray.COLOR_4 | GeometryArray.TEXTURE_COORDINATE_2; final int maxParticles = gb.getNumber(); IndexedTriangleArray geom = new IndexedTriangleArray( maxParticles * 3 + 0, maxParticles * 3 + 0 ); geom.setOptimization( Optimization.NONE ); setGeometry( geom ); index = new int[ gb.getNumber() * 3 ]; geom.setIndex( index ); Texture texture = TextureLoader.getInstance().getTexture( generator.getRender().getTextureName(), Texture.MipmapMode.MULTI_LEVEL_MIPMAP ); texture.setBoundaryModeS( TextureBoundaryMode.CLAMP_TO_BORDER ); texture.setBoundaryModeT( TextureBoundaryMode.CLAMP_TO_BORDER ); TransparencyAttributes transparencyAttribs = new TransparencyAttributes( TransparencyAttributes.BLENDED, 0.1f, getBlendFactor( generator.getRender().getSourceFactor() ), getBlendFactor( generator.getRender().getDestinationFactor() ) ); transparencyAttribs.setEnabled( true ); transparencyAttribs.setSortEnabled( true ); Appearance app = new Appearance(); if ( WIRE_FRAME ) { PolygonAttributes pa = new PolygonAttributes( DrawMode.LINE ); app.setPolygonAttributes( pa ); } else { app.setTexture( texture ); app.setTextureAttributes( texAttribs ); app.setRenderingAttributes( renderingAttribs ); app.setTransparencyAttributes( transparencyAttribs ); } //app.setLineAttributes(new LineAttributes( 1f, LineAttributes.PATTERN_SOLID, false ) ); //app.setDrawMode( DrawMode.LINE ); setAppearance( app ); } /** * {@inheritDoc} */ public Sized2iRO getSizeOnScreen() { return ( null ); } public void dispose() { generator = null; } public boolean isAbsoluteOrientation() { return ( relativeOrientation ); } public void setAbsoluteOrientation( boolean absoluteOrientation ) { this.relativeOrientation = absoluteOrientation; } public GeneratorShape3D( boolean relativeOrientation ) { this.relativeOrientation = relativeOrientation; } public GeneratorShape3D( boolean relativeOrientation, Generator generator ) { setGenerator( generator ); this.relativeOrientation = relativeOrientation; } }