Revision: 501 http://skycastle.svn.sourceforge.net/skycastle/?rev=501&view=rev Author: zzorn Date: 2008-04-29 14:42:09 -0700 (Tue, 29 Apr 2008) Log Message: ----------- Implemented a StrokePartRenderer, which renders a part of a stroke. This allows re-using meshes from a pool for stroke renderig. Also added an utility class for JME related utilities, and added an option to run commands later in the swing thread to the CommandStack. Modified Paths: -------------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/EmptyStroke.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/Stroke.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/StrokeImpl.java 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/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackImpl.java trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/command/CommandStackTest.java Added Paths: ----------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackMode.java Added: trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java 2008-04-29 21:42:09 UTC (rev 501) @@ -0,0 +1,87 @@ +package org.skycastle.opengl; + +import com.jme.renderer.ColorRGBA; +import com.jme.renderer.Renderer; +import com.jme.scene.Spatial; +import com.jme.scene.state.AlphaState; +import com.jme.scene.state.CullState; +import com.jme.scene.state.MaterialState; + +/** + * Java Monkey Engine related utilities. + * + * @author Hans Haggstrom + */ +public final class JmeUtils +{ + + //---------------------------------------------------------------------- + // Static Methods + + /** + * Sets the color of a {@link Spatial}. Code borrowed from JME forums. + * + * @param renderer the renderer that can be used to create render states. + * @param spatial the {@link Spatial} whose color we should change. + * @param color the color to use + * @param shininess amount of specular lighting (scale unknown?) + */ + public static void setColor( final Renderer renderer, + final Spatial spatial, + final ColorRGBA color, + final float shininess ) + { + final MaterialState materialState = renderer.createMaterialState(); + materialState.setDiffuse( color ); + materialState.setAmbient( color.mult( new ColorRGBA( 0.3f, 0.3f, 0.3f, 1 ) ) ); + materialState.setShininess( shininess ); + + final float mul = 1 + shininess > 18 ? ( shininess - 28 ) * 0.01f : 0; + materialState.setSpecular( color.mult( new ColorRGBA( mul, mul, mul, 1 ) ) ); + spatial.setRenderState( materialState ); + + if ( color.a < 1 ) + { + enableTranslucency( renderer, spatial ); + } + + CullState cullState = renderer.createCullState(); + cullState.setCullMode( CullState.CS_BACK ); + spatial.setRenderState( cullState ); + } + + + /** + * Enables transparency for the specified {@link Spatial}. + * + * @param renderer the renderer that can be used to create render states. + * @param spatial a spatial that should be allowed to contain transparent pars. + */ + public static void enableTranslucency( final Renderer renderer, final Spatial spatial ) + { + // 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 ); + +/* + alphaState.setTestEnabled(true); + alphaState.setTestFunction(AlphaState.TF_GREATER); +*/ + + spatial.setRenderState( alphaState ); + spatial.updateRenderState(); + spatial.setRenderQueueMode( Renderer.QUEUE_TRANSPARENT ); + } + + //====================================================================== + // Private Methods + + private JmeUtils() + { + } + +} Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java 2008-04-29 21:42:09 UTC (rev 501) @@ -24,7 +24,7 @@ //====================================================================== // Private Fields - private final CommandStack myCommandStack = new CommandStackImpl(); + private final CommandStack myCommandStack = new CommandStackImpl( CommandStackMode.INVOKE_LATER_IN_SWING_THREAD ); private final CommandAction myExitAction = new CommandAction( myCommandStack, Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java 2008-04-29 21:42:09 UTC (rev 501) @@ -14,7 +14,7 @@ import org.skycastle.sketch.model.group.Group; import org.skycastle.sketch.model.group.GroupElement; import org.skycastle.sketch.model.stroke.Stroke; -import org.skycastle.sketch.rendering.StrokeRenderer; +import org.skycastle.sketch.rendering.StrokePartRenderer; import org.skycastle.util.ParameterChecker; import org.skycastle.util.listenable.collection.CollectionListener; import org.skycastle.util.listenable.collection.ListenableCollection; @@ -489,12 +489,14 @@ { final Stroke stroke = (Stroke) groupElement; -/* final StrokePartRenderer strokePartRenderer = new StrokePartRenderer( 1000 ); strokePartRenderer.setStroke( stroke, 0 ); myRoot3DNode.attachChild( strokePartRenderer ); + +/* + + myRoot3DNode.attachChild( new StrokeRenderer( stroke ) ); */ - myRoot3DNode.attachChild( new StrokeRenderer( stroke ) ); } else if ( groupElement instanceof Group ) { Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/EmptyStroke.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/EmptyStroke.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/EmptyStroke.java 2008-04-29 21:42:09 UTC (rev 501) @@ -71,4 +71,10 @@ { } + public int getSize() + { + return 0; + + } + } Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/Stroke.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/Stroke.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/Stroke.java 2008-04-29 21:42:09 UTC (rev 501) @@ -29,7 +29,8 @@ /** * Adds the specified StrokeListener. * - * @param addedStrokeListener a listener that will be notified about added points in the stroke. Should not be null or already added. + * @param addedStrokeListener a listener that will be notified about added points in the stroke. Should + * not be null or already added. */ void addStrokeListener( StrokeListener addedStrokeListener ); @@ -40,4 +41,8 @@ */ void removeStrokeListener( StrokeListener removedStrokeListener ); + /** + * @return number of points in this stroke. + */ + int getSize(); } Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/StrokeImpl.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/StrokeImpl.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/StrokeImpl.java 2008-04-29 21:42:09 UTC (rev 501) @@ -60,7 +60,9 @@ public void addStrokeListener( StrokeListener addedStrokeListener ) { ParameterChecker.checkNotNull( addedStrokeListener, "addedStrokeListener" ); - ParameterChecker.checkNotAlreadyContained( addedStrokeListener, myStrokeListeners, "myStrokeListeners" ); + ParameterChecker.checkNotAlreadyContained( addedStrokeListener, + myStrokeListeners, + "myStrokeListeners" ); myStrokeListeners.add( addedStrokeListener ); } @@ -73,4 +75,10 @@ myStrokeListeners.remove( removedStrokeListener ); } + public int getSize() + { + return myPoints.size(); + + } + } 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 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeAppearanceCalculator.java 2008-04-29 21:42:09 UTC (rev 501) @@ -37,11 +37,11 @@ // Private Constants private static final float DEFAULT_Z_LEVEL = 0; - private static final float DEFAULT_WIDTH = 70; + private static final float DEFAULT_WIDTH = 15; private static final float DEFAULT_RED = 0; private static final float DEFAULT_GREEN = 0; private static final float DEFAULT_BLUE = 0; - private static final float DEFAULT_ALPHA = 0.33f; + private static final float DEFAULT_ALPHA = 0.3f; //====================================================================== // Public Methods 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 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokePartRenderer.java 2008-04-29 21:42:09 UTC (rev 501) @@ -5,13 +5,12 @@ import com.jme.math.Vector3f; import com.jme.renderer.ColorRGBA; import com.jme.renderer.Renderer; -import com.jme.scene.Spatial; import com.jme.scene.TriMesh; -import com.jme.scene.state.AlphaState; import com.jme.system.DisplaySystem; import com.jme.util.GameTaskQueue; import com.jme.util.GameTaskQueueManager; import com.jme.util.geom.BufferUtils; +import org.skycastle.opengl.JmeUtils; import org.skycastle.sketch.model.stroke.EmptyStroke; import org.skycastle.sketch.model.stroke.Stroke; import org.skycastle.sketch.model.stroke.StrokeListener; @@ -29,7 +28,8 @@ * * @author Hans Haggstrom */ -public class StrokePartRenderer +@SuppressWarnings( { "serial" } ) +public final class StrokePartRenderer extends TriMesh { @@ -114,24 +114,16 @@ myStartIndex = startIndex; myStroke = stroke; + myAppearanceCalculator.setStroke( myStroke ); myStroke.addStrokeListener( myStrokeListener ); recalculateStrokeAppearance(); } - /* - *//** - * @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 initializeGeometry( final int numberOfStrokePoints ) { // Calculate sizes @@ -150,11 +142,7 @@ initializeIndices( myIndices, numberOfSegments ); // Initialize the TriMesh - setVertexBuffer( 0, myVertexes ); - setColorBuffer( 0, myColors ); - setTextureBuffer( 0, myTextureCoordinates ); - setNormalBuffer( 0, myNormals ); - setIndexBuffer( 0, myIndices ); + reconstruct(); // Initialize bounding box setModelBound( new BoundingBox() ); @@ -162,12 +150,18 @@ } + private void reconstruct() + { + reconstruct( myVertexes, myNormals, myColors, myTextureCoordinates, myIndices ); + } + + private void onStrokePointAdded() { - recalculateStrokeAppearance(); /* - throw new UnsupportedOperationException( "This method has not yet been implemented." ); // IMPLEMENT + calculateLastPointAppearance(); */ + recalculateStrokeAppearance(); } @@ -181,7 +175,7 @@ { final Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer(); - enableTranslucency( renderer, StrokePartRenderer.this ); + JmeUtils.enableTranslucency( renderer, StrokePartRenderer.this ); return null; } @@ -190,23 +184,40 @@ } - private void enableTranslucency( final Renderer renderer, final Spatial spatial ) + @SuppressWarnings( { "PointlessArithmeticExpression" } ) + private void calculateLastPointAppearance() { - // 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 ); + final Vector3f positionLeft = new Vector3f(); + final Vector3f positionMid = new Vector3f(); + final Vector3f positionRight = new Vector3f(); - spatial.setRenderState( alphaState ); - spatial.updateRenderState(); + final ColorRGBA colorLeft = new ColorRGBA(); + final ColorRGBA colorMid = new ColorRGBA(); + final ColorRGBA colorRight = new ColorRGBA(); - // TODO: Make the rendering two sided - how? -/* - setCullMode( CULL_NEVER ); -*/ + final int lastPointIndex = myStroke.getSize() - 1; + + int bufferIndex = lastPointIndex - myStartIndex; + + if ( bufferIndex < 0 || + bufferIndex >= myPartLength ) + { + throw new IllegalStateException( + "The last point is not in this StrokePartRenderer, can't update its appearance." ); + } + + myAppearanceCalculator.resetToIndex( lastPointIndex ); + + myAppearanceCalculator.calculateNextPoint( positionLeft, positionMid, positionRight, + colorLeft, colorMid, colorRight ); + + final int vertexIndex = bufferIndex * 3; + setPointData( vertexIndex + 0, positionLeft, colorLeft ); + setPointData( vertexIndex + 1, positionMid, colorMid ); + setPointData( vertexIndex + 2, positionRight, colorRight ); + + // Update bounds to include the new shape + updateModelBound(); } @@ -221,6 +232,9 @@ final ColorRGBA colorMid = new ColorRGBA(); final ColorRGBA colorRight = new ColorRGBA(); + // Make the whole buffer writeable + clearBufferLimit(); + int pointIndex = myStartIndex; myAppearanceCalculator.resetToIndex( pointIndex ); @@ -240,27 +254,47 @@ pointIndex++; } - // Fill in rest of the stroke part with invisible data - while ( pointIndex < getPartEndIndex() ) - { - final int vertexIndex = pointIndex * 3; + // Do not render the rest of the stroke + setBufferLimit( pointIndex ); - setPointData( vertexIndex + 0, ORIGO, TRANSPARENT_BLACK ); - setPointData( vertexIndex + 1, ORIGO, TRANSPARENT_BLACK ); - setPointData( vertexIndex + 2, ORIGO, TRANSPARENT_BLACK ); + // Allow TriMesh to refresh its sizes from the limit values + reconstruct(); - pointIndex++; - } - - // Notify the 3D object that we changed it's appearance - // TODO + // Update new size bounds of this stroke updateModelBound(); - updateGeometricState( 0, true ); - updateRenderState(); + } - updateModelBound(); + + private void clearBufferLimit() + { + myVertexes.clear(); + myColors.clear(); + myTextureCoordinates.clear(); + myNormals.clear(); + myIndices.clear(); } + + private void setBufferLimit( final int pointIndex ) + { + final int segmentIndex = pointIndex - 1; + final int vertexIndex = pointIndex * 3; + final int IndicesIndex = segmentIndex * NUMBER_OF_INDICES_IN_SEGMENT; + + myVertexes.position( 3 * vertexIndex ); + myColors.position( 4 * vertexIndex ); + myTextureCoordinates.position( 2 * vertexIndex ); + myNormals.position( 3 * vertexIndex ); + myIndices.position( IndicesIndex ); + + myVertexes.flip(); + myColors.flip(); + myTextureCoordinates.flip(); + myNormals.flip(); + myIndices.flip(); + } + + private int getPartEndIndex() { return myStartIndex + myPartLength; Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/rendering/StrokeRenderer.java 2008-04-29 21:42:09 UTC (rev 501) @@ -1,28 +1,21 @@ 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.opengl.JmeUtils; 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; @@ -43,39 +36,31 @@ private static int theStrokeMeshCounter = 0; private final Stroke myStroke; + private final StrokeAppearanceCalculator myAppearanceCalculator; + private final StrokeListener myStrokeListener = new StrokeListener() + { - private final Object myTextureStateLock = new Object(); + public void onPointAdded( final Stroke updatedStroke, final DataSample point ) + { + initializeGeometry( myStroke.getPoints().size() ); + // Put vertices in correct places + recalculateAppearance(); + } + + }; + 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 @@ -90,48 +75,34 @@ */ public StrokeRenderer( final Stroke stroke ) { - this( stroke, 0 ); - } - - private final StrokeAppearanceCalculator myAppearanceCalculator; - - /** - * Creates a new {@link StrokeRenderer}. - * - * @param stroke the stroke that should be rendered - * @param z the default height of the stroke. - */ - public StrokeRenderer( final Stroke stroke, final float z ) - { // JME seems to need an unique identifier for each node. NOTE: Not thread safe. //noinspection AssignmentToStaticFieldFromInstanceMethod super( "StrokeMesh_" + theStrokeMeshCounter++ ); // Check parameters ParameterChecker.checkNotNull( stroke, "stroke" ); - ParameterChecker.checkNormalNumber( z, "z" ); myStroke = stroke; - myZ1 = z; myAppearanceCalculator = new StrokeAppearanceCalculator( myStroke ); - myStroke.addStrokeListener( new StrokeListener() - { + myStroke.addStrokeListener( myStrokeListener ); - public void onPointAdded( final Stroke updatedStroke, final DataSample point ) - { - recalculateStroke(); - } + initializeGeometry( myStroke.getPoints().size() ); - } ); + // Put vertices in correct places + // recalculateAppearance(); - if ( stroke.getPoints().size() >= 2 ) - { - recalculateStroke(); - } + initializeTranslucency(); + } + + //====================================================================== + // Private Methods + + private void initializeTranslucency() + { GameTaskQueueManager.getManager().getQueue( GameTaskQueue.UPDATE ).enqueue( new Callable<Object>() { @@ -140,98 +111,29 @@ { final Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer(); - final StrokeRenderer strokeRenderer = StrokeRenderer.this; + JmeUtils.enableTranslucency( renderer, 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() + private void initializeGeometry( final int numberOfStrokePoints ) { - // Put vertices in correct places - initializeVetices(); + // Calculate sizes + int numberOfSegments = numberOfStrokePoints - 1; + int numberOfVertices = numberOfStrokePoints * 3; + int numberOfIndices = numberOfSegments * NUMBER_OF_INDICES_IN_SEGMENT; - // 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(); - } - - /* * - * Creates a texture from the specified image and applies it to this Terrainmesh. - * - * @param textureImage the image to create a texture from. If null, a placeholder texture is created. - */ - /* public void setTextureImage( final BufferedImage textureImage ) + if ( numberOfStrokePoints < 2 ) { - GameTaskQueueManager.getManager().getQueue( GameTaskQueue.UPDATE ).enqueue( new Callable<Object>() - { - - public Object call() throws Exception - { - final Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer(); - initTexture( textureImage, renderer ); - return null; - } - - } ); + numberOfSegments = 0; + numberOfVertices = 0; + numberOfIndices = 0; } - */ - - /** - * @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 ); @@ -240,75 +142,104 @@ myIndices = BufferUtils.createIntBuffer( numberOfIndices ); // Stich together the vertices into triangles - initializeIndices(); + initializeIndices( myIndices, numberOfSegments ); + // Initialize the TriMesh + reconstruct( myVertexes, myNormals, myColors, myTextureCoordinates, myIndices ); - updateBounds(); + // Update bounding box + setModelBound( new BoundingBox() ); + updateModelBound(); } -/* - public void setPlaceholderTexture( final Texture texture, final BoundingRectangle textureArea ) + + private void recalculateAppearance() { - synchronized ( myTextureStateLock ) - { - myPlaceholderTextureInUse = true; + final Vector3f positionLeft = new Vector3f(); + final Vector3f positionMid = new Vector3f(); + final Vector3f positionRight = new Vector3f(); - if ( myTextureState != null ) - { - // Update texture indexes - setTextureCoordinates( textureArea ); + final ColorRGBA colorLeft = new ColorRGBA(); + final ColorRGBA colorMid = new ColorRGBA(); + final ColorRGBA colorRight = new ColorRGBA(); - // Update the geometry - updateGeometricState( 0, true ); + int pointIndex = 0; - // Use placeholder if no texture specified - Texture textureToUse = texture; - if ( textureToUse == null ) - { - textureToUse = PLACEHOLDER_TEXTURE; - } + myAppearanceCalculator.resetToIndex( pointIndex ); - // Set the texture - myTextureState.setTexture( textureToUse ); + while ( myAppearanceCalculator.hasNextPoint() ) + { + myAppearanceCalculator.calculateNextPoint( positionLeft, positionMid, positionRight, + colorLeft, colorMid, colorRight ); - updateRenderState(); - } + final int vertexIndex = pointIndex * 3; + + setPointData( vertexIndex + 0, positionLeft, colorLeft ); + setPointData( vertexIndex + 1, positionMid, colorMid ); + setPointData( vertexIndex + 2, positionRight, colorRight ); + + pointIndex++; } } -*/ -/* - public boolean isPlaceholderTextureInUse() - { - return myPlaceholderTextureInUse; - } -*/ - //====================================================================== - // Private Methods - - private void initializeVetices() + @SuppressWarnings( { "PointlessArithmeticExpression" } ) + private void recalculateStrokeAppearance() { - myAppearanceCalculator.resetToStart(); + final Vector3f positionLeft = new Vector3f(); + final Vector3f positionMid = new Vector3f(); + final Vector3f positionRight = new Vector3f(); - 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(); - final ColorRGBA colorLeft = new ColorRGBA(); - final ColorRGBA colorMid = new ColorRGBA(); - final ColorRGBA colorRight = new ColorRGBA(); + int pointIndex = 0;//myStartIndex; + myAppearanceCalculator.resetToIndex( pointIndex ); + + while ( myAppearanceCalculator.hasNextPoint() /*&& + pointIndex < getPartEndIndex()*/ ) + { myAppearanceCalculator.calculateNextPoint( positionLeft, positionMid, positionRight, colorLeft, colorMid, colorRight ); - setPointData( index++, positionLeft, colorLeft ); - setPointData( index++, positionMid, colorMid ); - setPointData( index++, positionRight, colorRight ); + final int vertexIndex = pointIndex * 3; + + setPointData( vertexIndex + 0, positionLeft, colorLeft ); + setPointData( vertexIndex + 1, positionMid, colorMid ); + setPointData( vertexIndex + 2, positionRight, colorRight ); + + pointIndex++; } + +/* + // Fill in rest of the stroke part with invisible data + while ( pointIndex < getPartEndIndex() ) + { + final int vertexIndex = pointIndex * 3; + + setPointData( vertexIndex + 0, ORIGO, TRANSPARENT_BLACK ); + setPointData( vertexIndex + 1, ORIGO, TRANSPARENT_BLACK ); + setPointData( vertexIndex + 2, ORIGO, TRANSPARENT_BLACK ); + + pointIndex++; + } + + // Notify the 3D object that we changed it's appearance + // TODO + reconstruct(); +*/ + + updateModelBound(); +/* + updateGeometricState( 0, true ); + updateRenderState(); + + updateModelBound(); + + updateWorldBound(); +*/ } @@ -322,13 +253,13 @@ @SuppressWarnings( { "PointlessArithmeticExpression" } ) - private void initializeIndices() + private void initializeIndices( final IntBuffer indices, final int numberOfSegments ) { // 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++ ) + for ( int segment = 0; segment < numberOfSegments; segment++ ) { /* Segment, with vertex order marked in: @@ -343,170 +274,22 @@ final int segmentStartVertex = segment * 3; - myIndices.put( index++, segmentStartVertex + 3 ); - myIndices.put( index++, segmentStartVertex + 4 ); - myIndices.put( index++, segmentStartVertex + 0 ); + indices.put( index++, segmentStartVertex + 3 ); + indices.put( index++, segmentStartVertex + 4 ); + indices.put( index++, segmentStartVertex + 0 ); - myIndices.put( index++, segmentStartVertex + 0 ); - myIndices.put( index++, segmentStartVertex + 4 ); - myIndices.put( index++, segmentStartVertex + 1 ); + indices.put( index++, segmentStartVertex + 0 ); + indices.put( index++, segmentStartVertex + 4 ); + indices.put( index++, segmentStartVertex + 1 ); - myIndices.put( index++, segmentStartVertex + 1 ); - myIndices.put( index++, segmentStartVertex + 4 ); - myIndices.put( index++, segmentStartVertex + 2 ); + indices.put( index++, segmentStartVertex + 1 ); + indices.put( index++, segmentStartVertex + 4 ); + indices.put( index++, segmentStartVertex + 2 ); - myIndices.put( index++, segmentStartVertex + 2 ); - myIndices.put( index++, segmentStartVertex + 4 ); - myIndices.put( index++, segmentStartVertex + 5 ); + indices.put( index++, segmentStartVertex + 2 ); + indices.put( index++, segmentStartVertex + 4 ); + indices.put( index++, segmentStartVertex + 5 ); } } -/* - private void initTexture( BufferedImage textureImage, final Renderer renderer ) - { - synchronized ( myTextureStateLock ) - { - // The texture can be null e.g. if we ran out of memory - if ( textureImage == null ) - { - textureImage = PLACEHOLDER_PICTURE; - } - - // Remove any placeholder render state - if ( myPlaceholderTextureInUse ) - { -*/ -/* - clearRenderState( RenderState.RS_TEXTURE ); -*/ -/* -*/ -/* - myPlaceholderTextureState.setEnabled( false ); - myPlaceholderTextureState.setTexture( null ); - myPlaceholderTextureState = null; -*/ -/* - setTextureCoordinates( WHOLE_TEXTURE_AREA ); -*/ -/* -*/ -/* - } - - if ( myTextureGraphics == null ) - { - // First time initializations: - - // Create JME Image Renderer - myTextureGraphics = ImageGraphics.createInstance( textureImage.getWidth( null ), - textureImage.getHeight( null ), - 0 ); - myTextureGraphics.drawImage( textureImage, 0, 0, null ); - myTextureGraphics.update(); - - // Create texture - myTexture = TextureManager.loadTexture( null, - createTextureKey( textureImage.hashCode() ), - myTextureGraphics.getImage() ); - - // Make sure this texture is not cached, as we will be updating it when the TerrainMesh is re-used - TextureManager.releaseTexture( myTexture ); - - // Clamp texture at edges (no wrapping) - myTexture.setWrap( Texture.WM_ECLAMP_S_ECLAMP_T ); - myTexture.setMipmapState( Texture.MM_LINEAR_LINEAR ); - - createTextureRenderState( renderer, myTexture ); - - if ( myPlaceholderTextureInUse ) - { - myTextureState.setTexture( myTexture, 0 ); - } - } - else - { - // Release the previously reserved textures, so that they don't take up space on the 3D card - // NOTE: Maybe this also forces JME to re-upload the changed texture? - if ( !myPlaceholderTextureInUse ) - { - myTextureState.deleteAll( true ); - } - else - { - myTextureState.setTexture( myTexture, 0 ); - myTextureState.deleteAll( true ); - } - - // Update the JME Image used by the texture - myTextureGraphics.drawImage( textureImage, 0, 0, null ); - myTextureGraphics.update(); - myTextureGraphics.update( myTexture ); - - // Make sure this texture is not cached, as we will be updating it when the TerrainMesh is re-used - TextureManager.releaseTexture( myTexture ); - - // Smoother look at low viewing angles - myTexture.setMipmapState( Texture.MM_LINEAR_LINEAR ); - } - - myPlaceholderTextureInUse = false; - } - } -*/ - -/* - private void createTextureRenderState( final Renderer renderer, final Texture texture ) - { - myTextureState = renderer.createTextureState(); - myTextureState.setEnabled( true ); - myTextureState.setTexture( texture, 0 ); - setRenderState( myTextureState ); - updateRenderState(); - } - - - private TextureKey createTextureKey( final int imageHashcode ) - { - final TextureKey tkey = new TextureKey( null, Texture.MM_LINEAR, Texture.FM_LINEAR, - DEFAULT_ANISO_LEVEL, false, DEFAULT_TEXTURE_IMAGE_FORMAT ); - tkey.setFileType( "" + imageHashcode ); - return tkey; - } - - - private void setTextureCoordinates( BoundingRectangle boundingRectangle ) - { - if ( boundingRectangle == null ) - { - boundingRectangle = WHOLE_TEXTURE_AREA; - } - - for ( int y = 0; y < mySizeY_vertices; y++ ) - { - for ( int x = 0; x < mySizeX_vertices; x++ ) - { - final int index = calculateMeshIndex( x, y ); - - final float textureXPos = (float) MathUtils.interpolateClamp( x, - 1, - mySizeX_vertices - 2, - boundingRectangle.getX1(), - boundingRectangle.getX2() ); - final float textureYPos = (float) MathUtils.interpolateClamp( y, - 1, - mySizeY_vertices - 2, - boundingRectangle.getY2(), - boundingRectangle.getY1() ); - - final Vector2f textureCoordinate = new Vector2f( textureXPos, textureYPos ); - - BufferUtils.setInBuffer( textureCoordinate, myTextureCoordinates, index ); - } - } - - setTextureBuffer( 0, myTextureCoordinates ); - } -*/ - } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackImpl.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackImpl.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackImpl.java 2008-04-29 21:42:09 UTC (rev 501) @@ -1,5 +1,7 @@ package org.skycastle.util.command; +import org.skycastle.util.ParameterChecker; + import javax.swing.*; import java.awt.event.ActionEvent; import java.util.ArrayDeque; @@ -42,6 +44,8 @@ }; + private final CommandStackMode myCommandStackMode; + //====================================================================== // Public Methods @@ -50,9 +54,16 @@ /** * Creates a new {@link CommandStackImpl}. + * + * @param commandStackMode wether to directly execute invoked commands, or wether to run them later in the + * swing thread. */ - public CommandStackImpl() + public CommandStackImpl( final CommandStackMode commandStackMode ) { + ParameterChecker.checkNotNull( commandStackMode, "commandStackMode" ); + + myCommandStackMode = commandStackMode; + updateActionStatuses(); } @@ -61,20 +72,25 @@ public void invoke( final Command command ) { - command.doCommand(); + switch ( myCommandStackMode ) + { + case DIRECT: + invokeDirect( command ); + break; + case INVOKE_LATER_IN_SWING_THREAD: + SwingUtilities.invokeLater( new Runnable() + { - myRedoStack.clear(); + public void run() + { + invokeDirect( command ); + } - if ( command.isDisruptive() ) - { - myUndoStack.clear(); + } ); + break; + default: + throw new IllegalStateException( "Unknown CommandStackMode: " + myCommandStackMode ); } - else if ( command.isUndoable() ) - { - myUndoStack.push( command ); - } - - updateActionStatuses(); } @@ -158,6 +174,25 @@ //====================================================================== // Private Methods + private void invokeDirect( final Command command ) + { + command.doCommand(); + + myRedoStack.clear(); + + if ( command.isDisruptive() ) + { + myUndoStack.clear(); + } + else if ( command.isUndoable() ) + { + myUndoStack.push( command ); + } + + updateActionStatuses(); + } + + private void updateActionStatuses() { myRedoAction.setEnabled( canRedo() ); Added: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackMode.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackMode.java (rev 0) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/command/CommandStackMode.java 2008-04-29 21:42:09 UTC (rev 501) @@ -0,0 +1,17 @@ +package org.skycastle.util.command; + +/** + * @author Hans Haggstrom + */ +public enum CommandStackMode +{ + /** + * Invoke the command directly in the same thread. + */ + DIRECT, + + /** + * The command is invoked later in the Swing thread. + */ + INVOKE_LATER_IN_SWING_THREAD, +} Modified: trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/command/CommandStackTest.java =================================================================== --- trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/command/CommandStackTest.java 2008-04-29 15:51:11 UTC (rev 500) +++ trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/command/CommandStackTest.java 2008-04-29 21:42:09 UTC (rev 501) @@ -175,7 +175,7 @@ { myCounter = new int[]{ 4 }; - myCommandStack = new CommandStackImpl(); + myCommandStack = new CommandStackImpl( CommandStackMode.DIRECT ); myCountdownAction = new CommandAction( myCommandStack, "Countdown" ) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.