Revision: 499 http://skycastle.svn.sourceforge.net/skycastle/?rev=499&view=rev Author: zzorn Date: 2008-04-29 07:00:35 -0700 (Tue, 29 Apr 2008) Log Message: ----------- Started refactoring the render code. Modified Paths: -------------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java 2008-04-29 10:36:15 UTC (rev 498) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java 2008-04-29 14:00:35 UTC (rev 499) @@ -37,7 +37,7 @@ // Private Constants private static final float DEFAULT_Z_LEVEL = 0; - private static final float DEFAULT_WIDTH = 50; + private static final float DEFAULT_WIDTH = 70; private static final float DEFAULT_RED = 0; private static final float DEFAULT_GREEN = 0; private static final float DEFAULT_BLUE = 0; Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java 2008-04-29 10:36:15 UTC (rev 498) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java 2008-04-29 14:00:35 UTC (rev 499) @@ -1,8 +1,301 @@ package org.skycastle.sketch.rendering; +import com.jme.bounding.BoundingBox; +import com.jme.image.Image; +import com.jme.image.Texture; +import com.jme.math.Vector2f; +import com.jme.math.Vector3f; +import com.jme.renderer.ColorRGBA; +import com.jme.renderer.Renderer; +import com.jme.scene.TriMesh; +import com.jme.scene.state.AlphaState; +import com.jme.scene.state.TextureState; +import com.jme.system.DisplaySystem; +import com.jme.util.GameTaskQueue; +import com.jme.util.GameTaskQueueManager; +import com.jme.util.TextureManager; +import com.jme.util.geom.BufferUtils; +import com.jmex.awt.swingui.ImageGraphics; +import org.skycastle.sketch.model.stroke.Stroke; +import org.skycastle.sketch.model.stroke.StrokeListener; +import org.skycastle.sketch.sample.DataSample; +import org.skycastle.util.ImageUtils; +import org.skycastle.util.ParameterChecker; + +import java.awt.image.BufferedImage; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.concurrent.Callable; + /** + * Holds a part of a stroke. + * <p/> + * Can update either the whole stroke, or only added parts. + * * @author Hans Haggstrom */ public class StrokePartRenderer + extends TriMesh { + + //====================================================================== + // Private Fields + + private static int theStrokeMeshCounter = 0; + + private final int myStartIndex; + private final int myPartLength; + + private final Stroke myStroke; + + private final Object myTextureStateLock = new Object(); + + private FloatBuffer myVertexes; + private FloatBuffer myColors; + private FloatBuffer myTextureCoordinates; + private FloatBuffer myNormals; + private IntBuffer myIndices; + private int myNumberOfSegments; + + private TextureState myTextureState; + private Texture myTexture; + private ImageGraphics myTextureGraphics; + + private TextureState myPlaceholderTextureState = null; + + private boolean myPlaceholderTextureInUse = false; + + //====================================================================== + // Private Constants + + private static final BufferedImage PLACEHOLDER_PICTURE = ImageUtils.createPlaceholderPicture( 128, 128 ); + private static final float DEFAULT_ANISO_LEVEL = 1.0f; + private static final int DEFAULT_TEXTURE_IMAGE_FORMAT = com.jme.image.Image.GUESS_FORMAT_NO_S3TC; + private static final Texture PLACEHOLDER_TEXTURE = TextureManager.loadTexture( PLACEHOLDER_PICTURE, + Texture.MM_LINEAR_LINEAR, + Texture.FM_LINEAR, + 1, + Image.GUESS_FORMAT_NO_S3TC, + false ); + private static final int NUMBER_OF_TRIANGLES_PER_SEGMENT = 4; + private static final int NUMBER_OF_INDICES_IN_SEGMENT = 3 * NUMBER_OF_TRIANGLES_PER_SEGMENT; + private float myZ1; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // Constructors + + private final StrokeAppearanceCalculator myAppearanceCalculator; + + /** + * Creates a new {@link StrokeRenderer}. + * + * @param stroke the stroke that should be rendered + * @param startIndex + */ + public StrokeRenderer( final Stroke stroke, final int startIndex, final int partLength ) + { + // JME seems to need an unique identifier for each node. NOTE: Not thread safe. + //noinspection AssignmentToStaticFieldFromInstanceMethod + super( "StrokeMesh_" + theStrokeMeshCounter++ ); + + ParameterChecker.checkNotNull( stroke, "stroke" ); + ParameterChecker.checkPositiveOrZeroInteger( startIndex, "startIndex" ); + ParameterChecker.checkPositiveOrZeroInteger( partLength, "partLength" ); + + myStartIndex = startIndex; + myPartLength = partLength; + myStroke = stroke; + + myAppearanceCalculator = new StrokeAppearanceCalculator( myStroke ); + + + myStroke.addStrokeListener( new StrokeListener() + { + + public void onPointAdded( final Stroke updatedStroke, final DataSample point ) + { + recalculateStroke(); + } + + } ); + + if ( stroke.getPoints().size() >= 2 ) + { + recalculateStroke(); + } + + GameTaskQueueManager.getManager().getQueue( GameTaskQueue.UPDATE ).enqueue( new Callable<Object>() + { + + public Object call() + throws Exception + { + final Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer(); + + final StrokeRenderer strokeRenderer = StrokeRenderer.this; + + // strokeRenderer.setRenderQueueMode( Renderer.QUEUE_TRANSPARENT ); + final AlphaState alphaState = renderer.createAlphaState(); + alphaState.setBlendEnabled( true ); + alphaState.setSrcFunction( AlphaState.SB_SRC_ALPHA ); + alphaState.setDstFunction( AlphaState.DB_ONE_MINUS_SRC_ALPHA ); + alphaState.setTestEnabled( false ); + alphaState.setEnabled( true ); + + strokeRenderer.setRenderState( alphaState ); + strokeRenderer.updateRenderState(); + + // TODO: Make the rendering two sided - how? +/* + setCullMode( CULL_NEVER ); +*/ + + return null; + } + + } ); + } + + //---------------------------------------------------------------------- + // Other Public Methods + + /** + * Updates the positon and covered area of the terrain mesh. + * <p/> + * Called from the constructor, as well as when a TerrainMesh is re-used. + */ + public void updateBounds() + { + // Put vertices in correct places + initializeVetices(); + + // Initialize the TriMesh + setVertexBuffer( 0, myVertexes ); + setColorBuffer( 0, myColors ); + setTextureBuffer( 0, myTextureCoordinates ); + setNormalBuffer( 0, myNormals ); + setIndexBuffer( 0, myIndices ); + + // Update bounding box + setModelBound( new BoundingBox() ); + updateModelBound(); + } + + + /** + * @return the texture that this TerrainMesh is currently using, or null if it is using a placeholder + * texture. + */ + public Texture getTexture() + { + return myTexture; + } + + //====================================================================== + // Private Methods + + private void recalculateStroke() + { + // Calculate sizes + final int numberOfPoints = myStroke.getPoints().size(); + myNumberOfSegments = numberOfPoints - 1; + final int numberOfTriangles = myNumberOfSegments * NUMBER_OF_TRIANGLES_PER_SEGMENT; + final int numberOfVertices = numberOfPoints * 3; + final int numberOfIndices = myNumberOfSegments * NUMBER_OF_INDICES_IN_SEGMENT; + + // Create databuffers + myVertexes = BufferUtils.createVector3Buffer( numberOfVertices ); + myColors = BufferUtils.createColorBuffer( numberOfVertices ); + myTextureCoordinates = BufferUtils.createVector2Buffer( numberOfVertices ); + myNormals = BufferUtils.createVector3Buffer( numberOfVertices ); + myIndices = BufferUtils.createIntBuffer( numberOfIndices ); + + // Stich together the vertices into triangles + initializeIndices(); + + + updateBounds(); + } + + //====================================================================== + // Private Methods + + private void initializeVetices() + { + myAppearanceCalculator.resetToStart(); + + int index = 0; + while ( myAppearanceCalculator.hasNextPoint() ) + { + final Vector3f positionLeft = new Vector3f(); + final Vector3f positionMid = new Vector3f(); + final Vector3f positionRight = new Vector3f(); + + final ColorRGBA colorLeft = new ColorRGBA(); + final ColorRGBA colorMid = new ColorRGBA(); + final ColorRGBA colorRight = new ColorRGBA(); + + myAppearanceCalculator.calculateNextPoint( positionLeft, positionMid, positionRight, + colorLeft, colorMid, colorRight ); + + setPointData( index++, positionLeft, colorLeft ); + setPointData( index++, positionMid, colorMid ); + setPointData( index++, positionRight, colorRight ); + } + } + + + private void setPointData( final int index, final Vector3f position, final ColorRGBA color ) + { + BufferUtils.setInBuffer( position, myVertexes, index ); + BufferUtils.setInBuffer( new Vector3f(), myNormals, index ); + BufferUtils.setInBuffer( color, myColors, index ); + BufferUtils.setInBuffer( new Vector2f(), myTextureCoordinates, index ); + } + + + @SuppressWarnings( { "PointlessArithmeticExpression" } ) + private void initializeIndices() + { + // OPTIMIZE: Use triangle strips or fans to get more efficient results! + + // Create indices indicating the vertexes that make up triangle faces + int index = 0; + for ( int segment = 0; segment < myNumberOfSegments; segment++ ) + { + /* + Segment, with vertex order marked in: + + 0---3 + | \ | + 1---4 --> direction of stroke + | / | + 2---5 + + */ + + final int segmentStartVertex = segment * 3; + + myIndices.put( index++, segmentStartVertex + 3 ); + myIndices.put( index++, segmentStartVertex + 4 ); + myIndices.put( index++, segmentStartVertex + 0 ); + + myIndices.put( index++, segmentStartVertex + 0 ); + myIndices.put( index++, segmentStartVertex + 4 ); + myIndices.put( index++, segmentStartVertex + 1 ); + + myIndices.put( index++, segmentStartVertex + 1 ); + myIndices.put( index++, segmentStartVertex + 4 ); + myIndices.put( index++, segmentStartVertex + 2 ); + + myIndices.put( index++, segmentStartVertex + 2 ); + myIndices.put( index++, segmentStartVertex + 4 ); + myIndices.put( index++, segmentStartVertex + 5 ); + } + } + + } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java 2008-04-29 10:36:15 UTC (rev 498) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java 2008-04-29 14:00:35 UTC (rev 499) @@ -134,6 +134,23 @@ * * @throws IllegalArgumentException if the check fails. */ + public static void checkPositiveOrZeroInteger( long parameter, String parameterName ) + { + if ( parameter < 0 ) + { + throwIllegalArgumentException( parameterName, parameter, "be a positive or zero number" ); + } + } + + + /** + * Checks that the specified parameter is positive or zero. + * + * @param parameter the parameter value to check + * @param parameterName the name of the parameter (used in error messages) + * + * @throws IllegalArgumentException if the check fails. + */ public static void checkNonNegativeInteger( int parameter, String parameterName ) { if ( parameter < 0 ) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.