Revision: 502 http://skycastle.svn.sourceforge.net/skycastle/?rev=502&view=rev Author: zzorn Date: 2008-04-30 05:49:30 -0700 (Wed, 30 Apr 2008) Log Message: ----------- Preliminary work on a render to texture approach for buffering brush strokes. However, looks like my laptop doesn't support render to texture, need to try it out on the desktop. I wonder if there is any way to solve the problem without render to texture? Modified Paths: -------------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/Canvas3D.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/CanvasInitializer.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java Added Paths: ----------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/BufferedScreen.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/DummyBufferedScreen.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRenderer.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRoom.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/TextureBufferedScreen.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/InputHandler.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/PenInputHandler.java Removed Paths: ------------- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/InputHandler.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/PenInputHandler.java trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/Canvas3D.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/Canvas3D.java 2008-04-29 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/Canvas3D.java 2008-04-30 12:49:30 UTC (rev 502) @@ -422,13 +422,13 @@ } - private void runInitializers( final Renderer renderer ) + private void runInitializers( final DisplaySystem displaySystem ) { myInitialized = true; for ( final CanvasInitializer initializer : myInitializers ) { - initializer.initialize( this, renderer ); + initializer.initialize( this, displaySystem ); } myInitializers.clear(); @@ -629,7 +629,8 @@ SwingUtilities.invokeLater( myFrameListenerUpdater ); } - runInitializers( getRenderer() ); + // TODO: Is this the correct display system? + runInitializers( DisplaySystem.getDisplaySystem() ); } Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/CanvasInitializer.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/CanvasInitializer.java 2008-04-29 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/CanvasInitializer.java 2008-04-30 12:49:30 UTC (rev 502) @@ -1,6 +1,6 @@ package org.skycastle.opengl; -import com.jme.renderer.Renderer; +import com.jme.system.DisplaySystem; /** * Something that gets called when the {@link Canvas3D} is initialized. @@ -15,8 +15,8 @@ * <p/> * Do not assume that this is called in the swing thread. * - * @param canvas3D the {@link Canvas3D} that has been initialized. - * @param renderer the 3D {@link Renderer} of the canvas. + * @param canvas3D the {@link org.skycastle.opengl.Canvas3D} that has been initialized. + * @param displaySystem */ - void initialize( final Canvas3D canvas3D, final Renderer renderer ); + void initialize( final Canvas3D canvas3D, final DisplaySystem displaySystem ); } Modified: trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java 2008-04-29 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/opengl/JmeUtils.java 2008-04-30 12:49:30 UTC (rev 502) @@ -1,5 +1,7 @@ package org.skycastle.opengl; +import com.jme.math.Vector3f; +import com.jme.renderer.Camera; import com.jme.renderer.ColorRGBA; import com.jme.renderer.Renderer; import com.jme.scene.Spatial; @@ -77,6 +79,30 @@ spatial.setRenderQueueMode( Renderer.QUEUE_TRANSPARENT ); } + /** + * Copies the position, angle, frustrum, and parallel and plane states from the source to the target camera. + * + * @param sourceCamera camera to copy settings from + * @param targetCamera camera whose settings should be changed. + */ + public static void copyCameraSettings( final Camera sourceCamera, final Camera targetCamera ) + { + targetCamera.setParallelProjection( sourceCamera.isParallelProjection() ); + targetCamera.setPlaneState( sourceCamera.getPlaneState() ); + + targetCamera.setLocation( new Vector3f( sourceCamera.getLocation() ) ); + targetCamera.setUp( new Vector3f( sourceCamera.getUp() ) ); + targetCamera.setLeft( new Vector3f( sourceCamera.getLeft() ) ); + targetCamera.setDirection( new Vector3f( sourceCamera.getDirection() ) ); + + targetCamera.setFrustumFar( sourceCamera.getFrustumFar() ); + targetCamera.setFrustumNear( sourceCamera.getFrustumNear() ); + targetCamera.setFrustumLeft( sourceCamera.getFrustumLeft() ); + targetCamera.setFrustumRight( sourceCamera.getFrustumRight() ); + targetCamera.setFrustumTop( sourceCamera.getFrustumTop() ); + targetCamera.setFrustumBottom( sourceCamera.getFrustumBottom() ); + } + //====================================================================== // Private Methods Added: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/BufferedScreen.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/BufferedScreen.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/BufferedScreen.java 2008-04-30 12:49:30 UTC (rev 502) @@ -0,0 +1,24 @@ +package org.skycastle.sketch; + +import com.jme.scene.Spatial; +import com.jme.system.DisplaySystem; +import org.skycastle.sketch.model.group.GroupElement; + +/** + * Holds a screen sized (+margin) texture, that can be used to render {@link GroupElement}s to. + * + * @author Hans Haggstrom + */ +public interface BufferedScreen +{ + /** + * @param spatial a 3D object to render to the buffer. + */ + void renderElement( Spatial spatial ); + + /** + * @return a 3D object with a view of the buffer. + */ + Spatial get3DView( DisplaySystem displaySystem ); + +} Added: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/DummyBufferedScreen.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/DummyBufferedScreen.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/DummyBufferedScreen.java 2008-04-30 12:49:30 UTC (rev 502) @@ -0,0 +1,54 @@ +package org.skycastle.sketch; + +import com.jme.scene.Node; +import com.jme.scene.Spatial; +import com.jme.system.DisplaySystem; + +/** + * An implementation of {@link BufferedScreen} that just keeps the rendered elements in a {@link com.jme.scene.Node}, + * instead of rendering them to a buffer. + * <p/> + * Allows working on the rest of the logic around the rende + * ring. + * + * @author Hans Haggstrom + */ +public final class DummyBufferedScreen + implements BufferedScreen +{ + + //====================================================================== + // Private Fields + + private Node myNode = null; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // BufferedScreen Implementation + + public void renderElement( final Spatial spatial ) + { + getOrCreateNode().attachChild( spatial ); + } + + public Spatial get3DView( DisplaySystem displaySystem ) + { + return getOrCreateNode(); + } + + //====================================================================== + // Private Methods + + private Node getOrCreateNode() + { + if ( myNode == null ) + { + myNode = new Node(); + } + + return myNode; + } + +} Deleted: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/InputHandler.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/InputHandler.java 2008-04-29 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/InputHandler.java 2008-04-30 12:49:30 UTC (rev 502) @@ -1,208 +0,0 @@ -package org.skycastle.sketch; - -import jpen.PenManager; -import org.skycastle.sketch.Tools.Tool; -import org.skycastle.sketch.sample.DataSample; -import org.skycastle.sketch.sample.SampleListener; -import org.skycastle.util.ParameterChecker; - -import javax.swing.*; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Hans Haggstrom - */ -public final class InputHandler -{ - - //====================================================================== - // Private Fields - - private final SketchController mySketchController; - private final List<Tool> myTools = new ArrayList<Tool>(); - private final PenInputHandler myPenInputHandler = new PenInputHandler(); - private final BlockingQueue<DataSample> myQueuedInput = new ArrayBlockingQueue<DataSample>( - MAX_INPUT_EVENT_QUEUE ); - - /** - * Recieves input in the pen input thread, and just queues it in a syncronized storage for processing in - * the Swing thread. - */ - private final SampleListener myInputReciever = new SampleListener() - { - - public void onSample( final DataSample dataSample ) - { - try - { - myQueuedInput.put( dataSample ); - } - catch ( InterruptedException e ) - { - // Interrupted, just return. The sample is lost, but it doesn't matter that much. - } - } - - - public void onSamples( final List<DataSample> dataSamples ) - { - // There's no putAll method, so just put the samples one at a time. - for ( final DataSample dataSample : dataSamples ) - { - onSample( dataSample ); - } - } - - }; - - //====================================================================== - // Private Constants - - private static final Logger LOGGER = Logger.getLogger( InputHandler.class.getName() ); - private static final int MAX_INPUT_EVENT_QUEUE = 10000; - private static final int MAX_SAMPLES_TO_HANDLE_AT_ONCE = 100; - - //====================================================================== - // Public Methods - - //---------------------------------------------------------------------- - // Constructors - - /** - * Creates a new {@link org.skycastle.sketch.InputHandler}. - */ - public InputHandler( final PenManager penManager, final SketchController sketchController ) - { - mySketchController = sketchController; - ParameterChecker.checkNotNull( penManager, "penManager" ); - - // Listen to projection changes - sketchController.getView().addProjectionChangeListener( myPenInputHandler ); - - // Convert pen events to DataSamples - penManager.pen.addListener( myPenInputHandler ); - - // Queue the DataSamples - myPenInputHandler.addListener( myInputReciever ); - - // Handle DataSamples in the queue with tools that run in the swing thread - final Thread sampleMover = new Thread( new Runnable() - { - - public void run() - { - final List<DataSample> samples = new ArrayList<DataSample>(); - - //noinspection InfiniteLoopStatement - while ( true ) - { - // Get next samples - waitForSamples( samples ); - - // Handle the samples in the Swing thread. Blocks until ready - invokeToolsInSwingThread( samples ); - - // Reuse collection. - samples.clear(); - } - } - - } ); - - sampleMover.start(); - } - - //---------------------------------------------------------------------- - // Other Public Methods - - /** - * Adds the specified Tool. - * - * @param addedTool should not be null or already added. - */ - public void addTool( final Tool addedTool ) - { - ParameterChecker.checkNotNull( addedTool, "addedTool" ); - ParameterChecker.checkNotAlreadyContained( addedTool, myTools, "myTools" ); - - myTools.add( addedTool ); - } - - - /** - * Removes the specified Tool. - * - * @param removedTool should not be null, and should be present. - */ - public void removeTool( final Tool removedTool ) - { - ParameterChecker.checkNotNull( removedTool, "removedTool" ); - ParameterChecker.checkContained( removedTool, myTools, "myTools" ); - - myTools.remove( removedTool ); - } - - //====================================================================== - // Private Methods - - private void invokeToolsInSwingThread( final List<DataSample> samples ) - { - try - { - SwingUtilities.invokeAndWait( new Runnable() - { - - public void run() - { - for ( final DataSample dataSample : samples ) - { - // Let each tool handle each sample - for ( final Tool tool : myTools ) - { - tool.onEvent( dataSample, mySketchController ); - } - } - } - - } ); - } - catch ( InterruptedException e ) - { - LOGGER.log( Level.INFO, "Interrupted while handling input events", e ); - } - catch ( InvocationTargetException e ) - { - LOGGER.log( Level.WARNING, "Problem when handling input events", e ); - } - } - - - private void waitForSamples( final List<DataSample> samples ) - { - // Wait for first sample - DataSample dataSample = null; - try - { - dataSample = myQueuedInput.take(); - } - catch ( InterruptedException e ) - { - // We were interrupted, didn't get anything. - } - - if ( dataSample != null ) - { - samples.add( dataSample ); - } - - // Get the rest of the available samples - myQueuedInput.drainTo( samples, MAX_SAMPLES_TO_HANDLE_AT_ONCE ); - } - -} Deleted: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/PenInputHandler.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/PenInputHandler.java 2008-04-29 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/PenInputHandler.java 2008-04-30 12:49:30 UTC (rev 502) @@ -1,159 +0,0 @@ -package org.skycastle.sketch; - -import jpen.*; -import jpen.event.PenListener; -import org.skycastle.sketch.sample.AbstractSampleProducer; -import org.skycastle.sketch.sample.DataSample; - -/** - * Recieves pen events, and turns them into {@link DataSample} based events. - * - * @author Hans Haggstrom - */ -@SuppressWarnings( { "ParameterNameDiffersFromOverriddenParameter" } ) -public final class PenInputHandler - extends AbstractSampleProducer - implements PenListener, ProjectionChangeListener -{ - - //====================================================================== - // Private Fields - - private final Object myProjectionLock = new Object(); - - private float myXOffs = 0; - private float myYOffs = 0; - private float myXScale = 1; - private float myYScale = 1; - - //====================================================================== - // Public Methods - - //---------------------------------------------------------------------- - // Constructors - - /** - * Creates a new {@link PenInputHandler}. - */ - public PenInputHandler() - { - } - - //---------------------------------------------------------------------- - // ProjectionChangeListener Implementation - - - public void onProjectionChanged( final float xOffs, - final float yOffs, - final float xScale, - final float yScale ) - { - synchronized ( myProjectionLock ) - { - myXOffs = xOffs; - myYOffs = yOffs; - myXScale = xScale; - myYScale = yScale; - } - } - - //---------------------------------------------------------------------- - // PenListener Implementation - - public void penKindEvent( final PKindEvent ev ) - { - // Ignored for now - } - - - public void penLevelEvent( final PLevelEvent event ) - { - final DataSample dataSample = new DataSample(); - - dataSample.setVariable( "time", getTimeAsSeconds( event ) ); - - for ( final PLevel level : event.levels ) - { - final float value = level.value; - switch ( level.getType() ) - { - case X: - synchronized ( myProjectionLock ) - { - final float x = value * myXScale + myXOffs; - dataSample.setVariable( "x", x ); - } - break; - case Y: - synchronized ( myProjectionLock ) - { - final float y = value * myYScale + myYOffs; - dataSample.setVariable( "y", y ); - } - break; - case PRESSURE: - dataSample.setVariable( "pressure", value ); - break; - case TILT_X: - dataSample.setVariable( "tiltX", value ); - break; - case TILT_Y: - dataSample.setVariable( "tiltY", value ); - break; - } - } - - sendSample( dataSample ); - } - - - @Override - public void penButtonEvent( final PButtonEvent event ) - { - final DataSample dataSample = new DataSample(); - - dataSample.setVariable( "time", getTimeAsSeconds( event ) ); - - final float value = event.button.value ? 1 : 0; - - switch ( event.button.getType() ) - { - case CENTER: - dataSample.setVariable( "centerButton", value ); - break; - - case LEFT: - dataSample.setVariable( "leftButton", value ); - break; - - case RIGHT: - dataSample.setVariable( "rightButton", value ); - break; - } - - sendSample( dataSample ); - } - - - public void penScrollEvent( final PScrollEvent ev ) - { - // Ignored for now - } - - - public void penTock( final long availableMillis ) - { - // TODO: If this goes towards zero, it means our listeners are taking up too much time. - // Reduce amount of messages? - } - - //====================================================================== - // Private Methods - - private float getTimeAsSeconds( final PenEvent event ) - { - // Convert timestamp to seconds - return ( 1.0f * event.getTime() ) / 1000.0f; - } - -} 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 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchController.java 2008-04-30 12:49:30 UTC (rev 502) @@ -1,6 +1,7 @@ package org.skycastle.sketch; import org.skycastle.sketch.Tools.StrokeTool; +import org.skycastle.sketch.input.InputHandler; import org.skycastle.sketch.model.Sketch; import org.skycastle.sketch.model.SketchImpl; import org.skycastle.sketch.model.group.Group; 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-30 12:49:30 UTC (rev 502) @@ -0,0 +1,136 @@ +package org.skycastle.sketch; + +import com.jme.scene.Spatial; +import com.jme.system.DisplaySystem; +import org.skycastle.sketch.model.Sketch; +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.StrokePartRenderer; +import org.skycastle.util.listenable.collection.CollectionListener; +import org.skycastle.util.listenable.collection.ListenableCollection; + +/** + * Provides a renderer for a {@link Sketch}. + * + * @author Hans Haggstrom + */ +public final class SketchRenderer +{ + + //====================================================================== + // Private Fields + + private final CollectionListener<GroupElement> mySketchListener = createSketchListener(); + private final BufferedScreen myBufferedScreen = new TextureBufferedScreen(); + + private Sketch mySketch = null; + private Spatial myRenderer = null; + + //====================================================================== + // Public Methods + + public Sketch getSketch() + { + return mySketch; + } + + public void setSketch( final Sketch sketch ) + { + if ( mySketch != null ) + { + mySketch.getRootGroup().removeGroupListener( mySketchListener ); + } + + mySketch = sketch; + + if ( mySketch != null ) + { + mySketch.getRootGroup().addGroupListener( mySketchListener ); + } + + redraw(); + } + + /** + * @return a {@link Spatial} that can be used to render the {@link SketchRoom}. + */ + public Spatial getRenderer( DisplaySystem displaySystem ) + { + if ( myRenderer == null ) + { + myRenderer = createRenderer( displaySystem ); + } + + return myRenderer; + } + + //====================================================================== + // Private Methods + + private CollectionListener<GroupElement> createSketchListener() + { + //noinspection serial + return new CollectionListener<GroupElement>() + { + + public void onElementAdded( final ListenableCollection<GroupElement> groupElementListenableCollection, final GroupElement addedElement ) + { + handleElementAddition( addedElement ); + } + + public void onElementRemoved( final ListenableCollection<GroupElement> groupElementListenableCollection, + final GroupElement removedElement ) + { + redraw(); + } + + }; + } + + private void redraw() + { + if ( myRenderer != null ) + { + // TODO + + } + } + + private Spatial createRenderer( DisplaySystem displaySystem ) + { + return myBufferedScreen.get3DView( displaySystem ); + } + + private void handleElementAddition( final GroupElement addedElement ) + { + // If the element is still actively changing, show it on top of the bg bitmap, + // and add a listener that notifies when it is ready + // If the element is ready (and when it is noted as ready), + // merge it into the bg bitmap + + + if ( addedElement instanceof Stroke ) + { + // TODO: If the stroke is unfinished, keep it in a separate buffer until finisehd + // Put that logic in the buffer + + Stroke stroke = (Stroke) addedElement; + + final StrokePartRenderer strokePartRenderer = new StrokePartRenderer( 1000 ); + strokePartRenderer.setStroke( stroke, 0 ); + + myBufferedScreen.renderElement( strokePartRenderer ); + } + else if ( addedElement instanceof Group ) + { + Group group = (Group) addedElement; + + for ( GroupElement groupElement : group.getElements() ) + { + handleElementAddition( groupElement ); + } + } + } + +} Copied: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRoom.java (from rev 501, trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java) =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRoom.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchRoom.java 2008-04-30 12:49:30 UTC (rev 502) @@ -0,0 +1,35 @@ +package org.skycastle.sketch; + +/** + * A simple sketch program, supporting a pen tablet as input, and running in opengl. + * + * @author Hans Haggstrom + */ +public final class SketchRoom +{ + + //---------------------------------------------------------------------- + // Main Method + + /** + * Start everything. + */ + public static void main( String[] args ) + { + // TODO: Do not show logging output below the WARNING level (JME outputs a lot of debugging info at INFO level). + // This way the console output is a bit more relevant. + + + final SketchController sketchController = new SketchController(); + + sketchController.start(); + } + + //====================================================================== + // Private Methods + + private SketchRoom() + { + } + +} 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 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SketchView.java 2008-04-30 12:49:30 UTC (rev 502) @@ -3,21 +3,14 @@ import com.jme.math.FastMath; import com.jme.math.Vector3f; import com.jme.renderer.Camera; -import com.jme.renderer.Renderer; -import com.jme.scene.Node; +import com.jme.system.DisplaySystem; import jpen.PenManager; import org.skycastle.opengl.Canvas3D; import org.skycastle.opengl.CanvasInitializer; import org.skycastle.opengl.ResizeHandler; import org.skycastle.opengl.navigationgestures.NavigationGesture; import org.skycastle.sketch.model.Sketch; -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.StrokePartRenderer; import org.skycastle.util.ParameterChecker; -import org.skycastle.util.listenable.collection.CollectionListener; -import org.skycastle.util.listenable.collection.ListenableCollection; import org.skycastle.util.simpleui.CloseHandler; import org.skycastle.util.simpleui.SimpleFrame; @@ -43,9 +36,11 @@ //====================================================================== // Private Fields + private final SketchRenderer mySketchRenderer = new SketchRenderer(); + + private final Map<String, JMenu> myMenus = new HashMap<String, JMenu>( 10 ); private final SimpleFrame myMainFrame; - private final Node myRoot3DNode; private final PenManager myPenManager; private final Object myProjectionLock = new Object(); @@ -125,10 +120,16 @@ myToolBar = new JToolBar(); myCanvas3D.removeAllNavigationGestures(); - myRoot3DNode = new Node( "SketchView_RootNode" ); - myCanvas3D.set3DNode( myRoot3DNode ); myCanvas3D.setBackgroundColor( Color.WHITE ); + myCanvas3D.addInitializer( new CanvasInitializer() + { + public void initialize( final Canvas3D canvas3D, final DisplaySystem displaySystem ) + { + myCanvas3D.set3DNode( mySketchRenderer.getRenderer( displaySystem ) ); + } + } ); + moveCamera( myCameraX, myCameraY, myViewSizeHorizontally, myRotationRadians ); final JPanel panel = new JPanel( new BorderLayout() ); @@ -239,7 +240,7 @@ myCanvas3D.addInitializer( new CanvasInitializer() { - public void initialize( final Canvas3D canvas3D, final Renderer renderer ) + public void initialize( final Canvas3D canvas3D, final DisplaySystem displaySystem ) { updateCamera( myCanvas3D, cameraX, cameraY, viewSizeHorizontally, rotationRadians ); } @@ -348,32 +349,11 @@ mySketch = sketch; - reRender( sketch ); + mySketchRenderer.setSketch( mySketch ); - //noinspection serial - sketch.getRootGroup().addGroupListener( new CollectionListener<GroupElement>() - { - - public void onElementAdded( final ListenableCollection<GroupElement> collection, - final GroupElement addedElement ) - { - // TODO: Instead of this, just modify the 3D object for the stroke that was changed, - // or add a new one, or remove an removed one. - reRender( sketch ); - } - - - public void onElementRemoved( final ListenableCollection<GroupElement> collection, - final GroupElement removedElement ) - { - // TODO: Instead of this, just modify the 3D object for the stroke that was changed, - // or add a new one, or remove an removed one. - reRender( sketch ); - } - - } ); } + /** * @param listener a listener that is notified whenever the projection changes. */ @@ -470,42 +450,6 @@ } - private void reRender( final Sketch sketch ) - { - // Clean out old nodes (NOTE: detach all is usually a bit buggy / weird.. ) - myRoot3DNode.detachAllChildren(); - - renderGroup( sketch.getRootGroup() ); - } - - - private void renderGroup( final Group group ) - { - for ( final GroupElement groupElement : group.getElements() ) - { - // TODO: Replace with some more polymorphic way to create nodes.. - - if ( groupElement instanceof Stroke ) - { - final Stroke stroke = (Stroke) groupElement; - - final StrokePartRenderer strokePartRenderer = new StrokePartRenderer( 1000 ); - strokePartRenderer.setStroke( stroke, 0 ); - myRoot3DNode.attachChild( strokePartRenderer ); - -/* - - myRoot3DNode.attachChild( new StrokeRenderer( stroke ) ); -*/ - } - else if ( groupElement instanceof Group ) - { - renderGroup( (Group) groupElement ); - } - } - } - - private JMenu getOrCreateMenu( final String menuName ) { JMenu menu = myMenus.get( menuName ); Deleted: 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-29 21:42:09 UTC (rev 501) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/SkycastleSketch.java 2008-04-30 12:49:30 UTC (rev 502) @@ -1,35 +0,0 @@ -package org.skycastle.sketch; - -/** - * A simple sketch program, supporting a pen tablet as input, and running in opengl. - * - * @author Hans Haggstrom - */ -public final class SkycastleSketch -{ - - //---------------------------------------------------------------------- - // Main Method - - /** - * Start everything. - */ - public static void main( String[] args ) - { - // TODO: Do not show logging output below the WARNING level (JME outputs a lot of debugging info at INFO level). - // This way the console output is a bit more relevant. - - - final SketchController sketchController = new SketchController(); - - sketchController.start(); - } - - //====================================================================== - // Private Methods - - private SkycastleSketch() - { - } - -} Added: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/TextureBufferedScreen.java =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/TextureBufferedScreen.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/TextureBufferedScreen.java 2008-04-30 12:49:30 UTC (rev 502) @@ -0,0 +1,92 @@ +package org.skycastle.sketch; + +import com.jme.image.Texture; +import com.jme.renderer.Camera; +import com.jme.renderer.ColorRGBA; +import com.jme.renderer.TextureRenderer; +import com.jme.scene.Spatial; +import com.jme.scene.shape.Quad; +import com.jme.scene.state.TextureState; +import com.jme.system.DisplaySystem; +import org.skycastle.opengl.JmeUtils; +import org.skycastle.util.ParameterChecker; + +/** + * @author Hans Haggstrom + */ +public final class TextureBufferedScreen + implements BufferedScreen +{ + + //====================================================================== + // Private Fields + + private Quad myQuad = null; + private TextureRenderer myTextureRenderer = null; + private Texture myTexture = null; + private DisplaySystem myDisplaySystem = null; + + //====================================================================== + // Private Constants + + private static final ColorRGBA TRANSPARENT = new ColorRGBA( 1f, 0f, 0f, 0.5f ); + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // BufferedScreen Implementation + + public void renderElement( final Spatial spatial ) + { + if ( myQuad != null ) + { + ParameterChecker.checkNotNull( spatial, "spatial" ); + + // Copy camera settings + final Camera drawingCamera = myDisplaySystem.getRenderer().getCamera(); + final Camera bufferCamera = myTextureRenderer.getCamera(); + JmeUtils.copyCameraSettings( drawingCamera, bufferCamera ); +// textureRenderer.updateCamera() // Doesn't exist? + + // Render texture + myTextureRenderer.render( spatial, myTexture ); + } + } + + public Spatial get3DView( DisplaySystem displaySystem ) + { + if ( myQuad == null ) + { + myQuad = createQuad( displaySystem ); + } + + return myQuad; + } + + //====================================================================== + // Private Methods + + private Quad createQuad( DisplaySystem displaySystem ) + { + ParameterChecker.checkNotNull( displaySystem, "displaySystem" ); + myDisplaySystem = displaySystem; + + //Create the texture renderer + myTextureRenderer = displaySystem.createTextureRenderer( 512, 512, TextureRenderer.RENDER_TEXTURE_2D ); + myTextureRenderer.setBackgroundColor( TRANSPARENT ); + + // Create the texture to render to + myTexture = new Texture(); + myTextureRenderer.setupTexture( myTexture ); + + // Create a quad to show the texture on + final Quad quad = new Quad(); + TextureState textureState = displaySystem.getRenderer().createTextureState(); + textureState.setTexture( myTexture ); + quad.setRenderState( textureState ); + + return quad; + } + +} Copied: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/InputHandler.java (from rev 501, trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/InputHandler.java) =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/InputHandler.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/InputHandler.java 2008-04-30 12:49:30 UTC (rev 502) @@ -0,0 +1,211 @@ +package org.skycastle.sketch.input; + +import jpen.PenManager; +import org.skycastle.sketch.SketchController; +import org.skycastle.sketch.Tools.Tool; +import org.skycastle.sketch.sample.DataSample; +import org.skycastle.sketch.sample.SampleListener; +import org.skycastle.util.ParameterChecker; + +import javax.swing.*; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Recieves inputs from {@link PenInputHandler}, and sends them to registered {@link Tool}s in the Swing thread. + * + * @author Hans Haggstrom + */ +public final class InputHandler +{ + + //====================================================================== + // Private Fields + + private final SketchController mySketchController; + private final List<Tool> myTools = new ArrayList<Tool>(); + private final PenInputHandler myPenInputHandler = new PenInputHandler(); + private final BlockingQueue<DataSample> myQueuedInput = new ArrayBlockingQueue<DataSample>( + MAX_INPUT_EVENT_QUEUE ); + + /** + * Recieves input in the pen input thread, and just queues it in a syncronized storage for processing in + * the Swing thread. + */ + private final SampleListener myInputReciever = new SampleListener() + { + + public void onSample( final DataSample dataSample ) + { + try + { + myQueuedInput.put( dataSample ); + } + catch ( InterruptedException e ) + { + // Interrupted, just return. The sample is lost, but it doesn't matter that much. + } + } + + + public void onSamples( final List<DataSample> dataSamples ) + { + // There's no putAll method, so just put the samples one at a time. + for ( final DataSample dataSample : dataSamples ) + { + onSample( dataSample ); + } + } + + }; + + //====================================================================== + // Private Constants + + private static final Logger LOGGER = Logger.getLogger( InputHandler.class.getName() ); + private static final int MAX_INPUT_EVENT_QUEUE = 10000; + private static final int MAX_SAMPLES_TO_HANDLE_AT_ONCE = 100; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // Constructors + + /** + * Creates a new {@link InputHandler}. + */ + public InputHandler( final PenManager penManager, final SketchController sketchController ) + { + mySketchController = sketchController; + ParameterChecker.checkNotNull( penManager, "penManager" ); + + // Listen to projection changes + sketchController.getView().addProjectionChangeListener( myPenInputHandler ); + + // Convert pen events to DataSamples + penManager.pen.addListener( myPenInputHandler ); + + // Queue the DataSamples + myPenInputHandler.addListener( myInputReciever ); + + // Handle DataSamples in the queue with tools that run in the swing thread + final Thread sampleMover = new Thread( new Runnable() + { + + public void run() + { + final List<DataSample> samples = new ArrayList<DataSample>(); + + //noinspection InfiniteLoopStatement + while ( true ) + { + // Get next samples + waitForSamples( samples ); + + // Handle the samples in the Swing thread. Blocks until ready + invokeToolsInSwingThread( samples ); + + // Reuse collection. + samples.clear(); + } + } + + } ); + + sampleMover.start(); + } + + //---------------------------------------------------------------------- + // Other Public Methods + + /** + * Adds the specified Tool. + * + * @param addedTool should not be null or already added. + */ + public void addTool( final Tool addedTool ) + { + ParameterChecker.checkNotNull( addedTool, "addedTool" ); + ParameterChecker.checkNotAlreadyContained( addedTool, myTools, "myTools" ); + + myTools.add( addedTool ); + } + + + /** + * Removes the specified Tool. + * + * @param removedTool should not be null, and should be present. + */ + public void removeTool( final Tool removedTool ) + { + ParameterChecker.checkNotNull( removedTool, "removedTool" ); + ParameterChecker.checkContained( removedTool, myTools, "myTools" ); + + myTools.remove( removedTool ); + } + + //====================================================================== + // Private Methods + + private void invokeToolsInSwingThread( final List<DataSample> samples ) + { + try + { + SwingUtilities.invokeAndWait( new Runnable() + { + + public void run() + { + for ( final DataSample dataSample : samples ) + { + // Let each tool handle each sample + for ( final Tool tool : myTools ) + { + tool.onEvent( dataSample, mySketchController ); + } + } + } + + } ); + } + catch ( InterruptedException e ) + { + LOGGER.log( Level.INFO, "Interrupted while handling input events", e ); + } + catch ( InvocationTargetException e ) + { + LOGGER.log( Level.WARNING, "Problem when handling input events", e ); + } + } + + + private void waitForSamples( final List<DataSample> samples ) + { + // Wait for first sample + DataSample dataSample = null; + try + { + dataSample = myQueuedInput.take(); + } + catch ( InterruptedException e ) + { + // We were interrupted, didn't get anything. + } + + if ( dataSample != null ) + { + samples.add( dataSample ); + } + + // Get the rest of the available samples + myQueuedInput.drainTo( samples, MAX_SAMPLES_TO_HANDLE_AT_ONCE ); + } + +} Copied: trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/PenInputHandler.java (from rev 501, trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/PenInputHandler.java) =================================================================== --- trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/PenInputHandler.java (rev 0) +++ trunk/skycastle/modules/ui/src/main/java/org/skycastle/sketch/input/PenInputHandler.java 2008-04-30 12:49:30 UTC (rev 502) @@ -0,0 +1,160 @@ +package org.skycastle.sketch.input; + +import jpen.*; +import jpen.event.PenListener; +import org.skycastle.sketch.ProjectionChangeListener; +import org.skycastle.sketch.sample.AbstractSampleProducer; +import org.skycastle.sketch.sample.DataSample; + +/** + * Recieves pen events, and turns them into {@link DataSample} based events. + * + * @author Hans Haggstrom + */ +@SuppressWarnings( { "ParameterNameDiffersFromOverriddenParameter" } ) +public final class PenInputHandler + extends AbstractSampleProducer + implements PenListener, ProjectionChangeListener +{ + + //====================================================================== + // Private Fields + + private final Object myProjectionLock = new Object(); + + private float myXOffs = 0; + private float myYOffs = 0; + private float myXScale = 1; + private float myYScale = 1; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // Constructors + + /** + * Creates a new {@link PenInputHandler}. + */ + public PenInputHandler() + { + } + + //---------------------------------------------------------------------- + // ProjectionChangeListener Implementation + + + public void onProjectionChanged( final float xOffs, + final float yOffs, + final float xScale, + final float yScale ) + { + synchronized ( myProjectionLock ) + { + myXOffs = xOffs; + myYOffs = yOffs; + myXScale = xScale; + myYScale = yScale; + } + } + + //---------------------------------------------------------------------- + // PenListener Implementation + + public void penKindEvent( final PKindEvent ev ) + { + // Ignored for now + } + + + public void penLevelEvent( final PLevelEvent event ) + { + final DataSample dataSample = new DataSample(); + + dataSample.setVariable( "time", getTimeAsSeconds( event ) ); + + for ( final PLevel level : event.levels ) + { + final float value = level.value; + switch ( level.getType() ) + { + case X: + synchronized ( myProjectionLock ) + { + final float x = value * myXScale + myXOffs; + dataSample.setVariable( "x", x ); + } + break; + case Y: + synchronized ( myProjectionLock ) + { + final float y = value * myYScale + myYOffs; + dataSample.setVariable( "y", y ); + } + break; + case PRESSURE: + dataSample.setVariable( "pressure", value ); + break; + case TILT_X: + dataSample.setVariable( "tiltX", value ); + break; + case TILT_Y: + dataSample.setVariable( "tiltY", value ); + break; + } + } + + sendSample( dataSample ); + } + + + @Override + public void penButtonEvent( final PButtonEvent event ) + { + final DataSample dataSample = new DataSample(); + + dataSample.setVariable( "time", getTimeAsSeconds( event ) ); + + final float value = event.button.value ? 1 : 0; + + switch ( event.button.getType() ) + { + case CENTER: + dataSample.setVariable( "centerButton", value ); + break; + + case LEFT: + dataSample.setVariable( "leftButton", value ); + break; + + case RIGHT: + dataSample.setVariable( "rightButton", value ); + break; + } + + sendSample( dataSample ); + } + + + public void penScrollEvent( final PScrollEvent ev ) + { + // Ignored for now + } + + + public void penTock( final long availableMillis ) + { + // TODO: If this goes towards zero, it means our listeners are taking up too much time. + // Reduce amount of messages? + } + + //====================================================================== + // Private Methods + + private float getTimeAsSeconds( final PenEvent event ) + { + // Convert timestamp to seconds + return ( 1.0f * event.getTime() ) / 1000.0f; + } + +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.