Revision: 482 http://skycastle.svn.sourceforge.net/skycastle/?rev=482&view=rev Author: zzorn Date: 2008-04-26 10:03:10 -0700 (Sat, 26 Apr 2008) Log Message: ----------- One stroke renders in a 3D view now, next step to work on stroke input, and connecting the view to the model Modified Paths: -------------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/Sketch.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/group/Group.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/point/StrokePoint.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/Stroke.java Added Paths: ----------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRenderer.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/StrokeRenderer.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/brush/ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/brush/Brush.java Added: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRenderer.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRenderer.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRenderer.java 2008-04-26 17:03:10 UTC (rev 482) @@ -0,0 +1,21 @@ +package org.skycastle.sketch; + +import org.skycastle.sketch.model.Sketch; + +/** + * Helps with rendering a {@link Sketch}. + * + * @author Hans Häggström + */ +public class SketchRenderer +{ + + /** + * Creates a new {@link org.skycastle.sketch.SketchRenderer}. + */ + public SketchRenderer() + { + } + + +} Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java 2008-04-26 15:22:04 UTC (rev 481) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java 2008-04-26 17:03:10 UTC (rev 482) @@ -7,10 +7,14 @@ import com.jme.scene.shape.Box; import com.jme.scene.state.LightState; import org.skycastle.opengl.Canvas3D; +import org.skycastle.sketch.model.point.StrokePointImpl; +import org.skycastle.sketch.model.stroke.Stroke; +import org.skycastle.sketch.model.stroke.StrokeImpl; import org.skycastle.util.simpleui.SimpleFrame; import javax.swing.*; import java.awt.BorderLayout; +import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; @@ -60,8 +64,10 @@ new SimpleFrame( panel ); - canvas3D.set3DNode( create3DScene() ); + final StrokeRenderer strokeRenderer = new StrokeRenderer( createTestStroke() ); + canvas3D.set3DNode( strokeRenderer ); + /* Sketch3DUI sketch3DUI = new Sketch3DUI(); sketch3DUI.start(); @@ -71,6 +77,26 @@ //====================================================================== // Private Methods + + private static Stroke createTestStroke() + { + final StrokeImpl stroke = new StrokeImpl(); + + float x = 0.0f; + float y = 0.0f; + final Random random = new Random(); + + for ( int i = 0; i < 1000; i++ ) + { + x += 10; + y += random.nextGaussian() * 3; + + stroke.addPoint( new StrokePointImpl( x, y ) ); + } + + return stroke; + } + /** * Create the 3D scene. In this case, this is just the rotating box. */ Added: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/StrokeRenderer.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/StrokeRenderer.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/StrokeRenderer.java 2008-04-26 17:03:10 UTC (rev 482) @@ -0,0 +1,465 @@ +package org.skycastle.sketch; + +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.scene.TriMesh; +import com.jme.scene.state.TextureState; +import com.jme.util.TextureManager; +import com.jme.util.geom.BufferUtils; +import com.jmex.awt.swingui.ImageGraphics; +import org.skycastle.sketch.model.point.StrokePoint; +import org.skycastle.sketch.model.stroke.Stroke; +import org.skycastle.util.ImageUtils; +import org.skycastle.util.ParameterChecker; + +import java.awt.image.BufferedImage; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +/** + * @author Hans Häggström + */ +@SuppressWarnings( { "serial" } ) +public class StrokeRenderer + extends TriMesh +{ + + //====================================================================== + // Private Fields + + private static int theStrokeMeshCounter = 0; + + private final Stroke myStroke; + + private final float myZ; + private final FloatBuffer myVertexes; + private final FloatBuffer myColors; + private final FloatBuffer myTextureCoordinates; + private final FloatBuffer myNormals; + private final IntBuffer myIndices; + private final int myNumberOfVertices; + private final int myNumberOfIndices; + + private final Object myTextureStateLock = new Object(); + private final int myNumberOfPoints; + private final int myNumberOfSegments; + private final int myNumberOfTriangles; + + 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; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // Constructors + + /** + * Creates a new {@link org.skycastle.sketch.StrokeRenderer}. + * + * @param stroke the stroke that should be rendered + */ + public StrokeRenderer( final Stroke stroke ) + { + this( stroke, 0 ); + } + + /** + * Creates a new {@link org.skycastle.sketch.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; + myZ = z; + + // Calculate sizes + myNumberOfPoints = myStroke.getPoints().size(); + myNumberOfSegments = myNumberOfPoints - 1; + myNumberOfTriangles = myNumberOfSegments * NUMBER_OF_TRIANGLES_PER_SEGMENT; + myNumberOfVertices = myNumberOfPoints * 3; + myNumberOfIndices = myNumberOfSegments * NUMBER_OF_INDICES_IN_SEGMENT; + + // Create databuffers + myVertexes = BufferUtils.createVector3Buffer( myNumberOfVertices ); + myColors = BufferUtils.createColorBuffer( myNumberOfVertices ); + myTextureCoordinates = BufferUtils.createVector2Buffer( myNumberOfVertices ); + myNormals = BufferUtils.createVector3Buffer( myNumberOfVertices ); + myIndices = BufferUtils.createIntBuffer( myNumberOfIndices ); + + // Stich together the vertices into triangles + initializeIndices(); + + updateBounds(); + } + + //---------------------------------------------------------------------- + // 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(); + } + + /* * + * 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 ) + { + 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; + } + + } ); + } + + */ + + /** + * @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 + +/* + public void setPlaceholderTexture( final Texture texture, final BoundingRectangle textureArea ) + { + synchronized ( myTextureStateLock ) + { + myPlaceholderTextureInUse = true; + + if ( myTextureState != null ) + { + // Update texture indexes + setTextureCoordinates( textureArea ); + + // Update the geometry + updateGeometricState( 0, true ); + + // Use placeholder if no texture specified + Texture textureToUse = texture; + if ( textureToUse == null ) + { + textureToUse = PLACEHOLDER_TEXTURE; + } + + // Set the texture + myTextureState.setTexture( textureToUse ); + + updateRenderState(); + } + } + } +*/ + +/* + public boolean isPlaceholderTextureInUse() + { + return myPlaceholderTextureInUse; + } +*/ + + //====================================================================== + // Private Methods + + private void initializeVetices() + { + int pointIndex = 0; + for ( final StrokePoint strokePoint : myStroke.getPoints() ) + { + initializePoint( strokePoint, pointIndex++ ); + } + } + + + @SuppressWarnings( { "PointlessArithmeticExpression" } ) + private void initializePoint( final StrokePoint strokePoint, final int pointIndex ) + { + final int index = pointIndex * 3; + + final float xPos = strokePoint.getX(); + final float yPos = strokePoint.getY(); + final float zPos = myZ; + final float width = strokePoint.getProperty( "width", 5.0f ); + + final Vector3f positionLeft = new Vector3f( xPos, yPos + width, zPos ); + final ColorRGBA colorLeft = strokePoint.getProperty( "color", + new ColorRGBA( 0.1f, 0.1f, 0.1f, 1.0f ) ); + + final Vector3f positionMid = new Vector3f( xPos, yPos, zPos ); + final ColorRGBA colorMid = strokePoint.getProperty( "color", + new ColorRGBA( 0.5f, 0.5f, 0.5f, 1.0f ) ); + + final Vector3f positionRight = new Vector3f( xPos, yPos - width, zPos ); + final ColorRGBA colorRight = strokePoint.getProperty( "color", + new ColorRGBA( 0.1f, 0.1f, 0.1f, 1.0f ) ); + + setPointData( index + 0, positionLeft, colorLeft ); + setPointData( index + 1, positionMid, colorMid ); + setPointData( index + 2, 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 ); + } + } + +/* + 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/ui/src/main/java/org/skycastle/sketch/model/Sketch.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/Sketch.java 2008-04-26 15:22:04 UTC (rev 481) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/Sketch.java 2008-04-26 17:03:10 UTC (rev 482) @@ -1,13 +1,12 @@ package org.skycastle.sketch.model; import org.skycastle.sketch.model.group.Group; +import org.skycastle.sketch.model.stroke.Stroke; /** * Holds the data of a drawing. * <p/> - * Strokes can be added and removed. - * <p/> - * Strokes can be grouped into Layers. + * Has a root {@link Group} that contains the {@link Stroke}s of the {@link Sketch}. * * @author Hans Häggström */ @@ -15,8 +14,8 @@ { /** - * @return the base {@link org.skycastle.sketch.model.group.Group} for this {@link Sketch}. Can be used to - * modify the contents of the {@link Sketch}. + * @return the base {@link Group} for this {@link Sketch}. Can be used to modify the contents of the + * {@link Sketch}. */ Group getRootGroup(); Added: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/brush/Brush.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/brush/Brush.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/brush/Brush.java 2008-04-26 17:03:10 UTC (rev 482) @@ -0,0 +1,12 @@ +package org.skycastle.sketch.model.brush; + +import org.skycastle.sketch.model.stroke.Stroke; + +/** + * Defines the style of a {@link Stroke}, and takes care of rendering it. + * + * @author Hans Häggström + */ +public interface Brush +{ +} Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/group/Group.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/group/Group.java 2008-04-26 15:22:04 UTC (rev 481) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/group/Group.java 2008-04-26 17:03:10 UTC (rev 482) @@ -1,13 +1,15 @@ package org.skycastle.sketch.model.group; +import org.skycastle.sketch.model.Sketch; +import org.skycastle.sketch.model.stroke.Stroke; import org.skycastle.util.listenable.collection.CollectionListener; import java.util.List; /** - * A way to organize the contents of a {@link org.skycastle.sketch.model.Sketch}. + * A way to organize the contents of a {@link Sketch}. * <p/> - * A {@link Group} can contain other {@link Group}s, or {@link org.skycastle.sketch.model.stroke.Stroke}s. + * A {@link Group} can contain other {@link Group}s, or {@link Stroke}s. * * @author Hans Haggstrom */ Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/point/StrokePoint.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/point/StrokePoint.java 2008-04-26 15:22:04 UTC (rev 481) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/point/StrokePoint.java 2008-04-26 17:03:10 UTC (rev 482) @@ -1,10 +1,12 @@ package org.skycastle.sketch.model.point; +import org.skycastle.sketch.model.stroke.Stroke; + /** - * A point on a {@link org.skycastle.sketch.model.stroke.Stroke} + * A point on a {@link Stroke} * <p/> * Can be modified by filters when the stroke is created, but usually not modified in the final {@link - * org.skycastle.sketch.model.stroke.Stroke}. + * Stroke}. * * @author Hans Häggström */ 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-26 15:22:04 UTC (rev 481) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/model/stroke/Stroke.java 2008-04-26 17:03:10 UTC (rev 482) @@ -1,12 +1,13 @@ package org.skycastle.sketch.model.stroke; +import org.skycastle.sketch.model.group.Group; import org.skycastle.sketch.model.group.GroupElement; import org.skycastle.sketch.model.point.StrokePoint; import java.util.List; /** - * Immutable, except points can be added (and removed?), and the changes listened to. + * A brush stroke that is defined by a path of {@link StrokePoint}s. * * @author Hans Häggström */ @@ -15,7 +16,7 @@ { /** - * Adds the specified {@link org.skycastle.sketch.model.point.StrokePoint} to this {@link Stroke}. + * Adds the specified {@link StrokePoint} to this {@link Stroke}. * * @param addedPoint should not be null or already added. */ @@ -29,8 +30,7 @@ void removePoint( StrokePoint removedPoint ); /** - * @return a read-only list with the {@link org.skycastle.sketch.model.point.StrokePoint}s in this {@link - * Stroke}. + * @return a read-only list with the {@link StrokePoint}s in this {@link Stroke}. */ List<StrokePoint> getPoints(); @@ -40,8 +40,7 @@ StrokeListener getStrokeListener(); /** - * Usually this is only called from the {@link org.skycastle.sketch.model.group.Group} that contains the - * {@link Stroke}. + * Usually this is only called from the {@link Group} that contains the {@link Stroke}. * * @param strokeListener the {@link StrokeListener} that should listen to changes to this {@link Stroke}. */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.