/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.base; import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import org.jagatoo.input.InputSystem; import org.jagatoo.input.InputSystemException; import org.openmali.vecmath2.Tuple3f; import org.xith3d.loop.RenderLoop; import org.xith3d.loop.UpdatingThread.TimingMode; import org.xith3d.loop.opscheduler.OperationScheduler; import org.xith3d.loop.opscheduler.PickScheduler; import org.xith3d.physics.PhysicsEngine; import org.xith3d.render.Canvas3DWrapper; import org.xith3d.render.Canvas3D; import org.xith3d.render.DefaultRenderer; import org.xith3d.render.RenderPass; import org.xith3d.render._RNDR_PrivilegedAccess; import org.xith3d.scenegraph.RenderableSceneGraph; import org.xith3d.scenegraph.SceneGraph; import org.xith3d.scenegraph.View; import org.xith3d.sound.SoundDriver; import org.xith3d.sound.SoundProcessor; import org.xith3d.ui.hud.__HUD_PrivilegedAccess; import org.xith3d.utility.logging.X3DLog; /** * This class offers the common objects needed for Xith3D rendering. * * Link it with an instance of RenderLoop * "renderLoop.addRenderEngine(RenderEngine)" to let the scene be rendered in a * separate thread. * * @see org.xith3d.loop.RenderLoop * * @author Marvin Froehlich (aka Qudus) */ public class Xith3DEnvironment extends SceneGraph implements RenderableSceneGraph { private RenderLoop renderLoop; private EnvScreenshotEngine screenshotEngine = null; private PickScheduler pickScheduler = null; private PhysicsEngine physicsEngine = null; private final ConcurrentHashMap< Canvas3D, Boolean > canvasEnabledMap; public boolean canvasAliveCheckSuppressed = true; private Canvas3D firstCanvas; private HashMap<Canvas3D, List<RenderPass>> canvasRenderPassMap = null; public void setRenderLoop( RenderLoop renderLoop ) { this.renderLoop = renderLoop; } public final RenderLoop getRenderLoop() { return ( renderLoop ); } /** * {@inheritDoc} */ public final OperationScheduler getOperationScheduler() { if ( renderLoop == null ) return ( null ); return ( renderLoop.getOperationScheduler() ); } /** * Sets this environment's ScreenshotEngine. * * @param engine */ public void setScreenshotEngine( EnvScreenshotEngine engine ) { this.screenshotEngine = engine; } /** * @return this environment's ScreenshotEngine */ public EnvScreenshotEngine getScreenshotEngine() { return ( screenshotEngine ); } /** * Sets this environment's PickScheduler. * * @param picker */ public void setPickScheduler( PickScheduler picker ) { this.pickScheduler = picker; } /** * @return this environment's PickScheduler */ public PickScheduler getPickScheduler() { return ( pickScheduler ); } /** * Sets the PhysicsEngine, which is automatically updated by the RenderLoop. * * @param physEngine */ public void setPhysicsEngine( PhysicsEngine physEngine ) { this.physicsEngine = physEngine; } /** * @return the PhysicsEngine, which is automatically updated by the RenderLoop. */ public PhysicsEngine getPhysicsEngine() { return ( physicsEngine ); } public final void updatePhysicsEngine( long gameTime, long frameTime, TimingMode timingMode ) { if ( physicsEngine != null ) { physicsEngine.update( gameTime, frameTime, timingMode ); } } public final void updateInputSystem( long gameTime, TimingMode timingMode ) { if ( !InputSystem.hasInstance() ) return; final long nanoGameTime = timingMode.getNanoSeconds( gameTime ); final InputSystem inputSystem = InputSystem.getInstance(); try { inputSystem.update( nanoGameTime ); } catch ( InputSystemException ex ) { ex.printStackTrace(); X3DLog.print( ex ); } } /** * @deprecated use {@link SoundProcessor#getInstance()} instead to set the sound driver. */ @Deprecated public void setSoundDriver( SoundDriver soundDriver ) { SoundProcessor.getInstance().setSoundDriver( soundDriver ); } /** * @deprecated use {@link SoundProcessor#getInstance()} instead to set the sound driver. */ @Deprecated public SoundDriver getSoundDriver() { return ( SoundProcessor.getInstance().getSoundDriver() ); } /** * {@inheritDoc} */ public Canvas3D addCanvas( Canvas3D canvas, View view ) { _RNDR_PrivilegedAccess.addCanvas3D( canvas, getRenderer() ); view.addCanvas3D( canvas ); if ( firstCanvas == null ) { firstCanvas = canvas; for ( int i = 0; i < huds.size(); i++ ) { huds.get( i ).connect( firstCanvas ); } } canvasEnabledMap.put( canvas, true ); return ( canvas ); } /** * {@inheritDoc} */ public Canvas3D addCanvas( Canvas3D canvas ) { addCanvas( canvas, getView() ); return ( canvas ); } /** * {@inheritDoc} */ public Canvas3DWrapper addCanvas( Canvas3DWrapper canvasWrapper, View view ) { addCanvas( canvasWrapper.getCanvas(), view ); return ( canvasWrapper ); } /** * {@inheritDoc} */ public Canvas3DWrapper addCanvas( Canvas3DWrapper canvasWrapper ) { addCanvas( canvasWrapper.getCanvas() ); return ( canvasWrapper ); } /** * {@inheritDoc} */ public void removeCanvas( Canvas3D canvas ) { _RNDR_PrivilegedAccess.removeCanvas3D( canvas, getRenderer() ); if ( canvas.getView() != null ) canvas.getView().removeCanvas3D( canvas ); canvasEnabledMap.remove( canvas ); if (canvasEnabledMap.isEmpty()) firstCanvas = null; } /** * {@inheritDoc} */ public void removeCanvas( Canvas3DWrapper canvasWrapper ) { _RNDR_PrivilegedAccess.removeCanvas3D( canvasWrapper.getCanvas(), getRenderer() ); if ( canvasWrapper.getCanvas().getView() != null ) canvasWrapper.getCanvas().getView().removeCanvas3D( canvasWrapper.getCanvas() ); } /** * {@inheritDoc} */ public void removeAllCanvas3Ds() { final int nc = getRenderer().getNumberOfCanvas3Ds(); for ( int i = 0; i < nc; i++ ) { final Canvas3D canvas = _RNDR_PrivilegedAccess.removeCanvas3D( i, getRenderer() ); canvas.getView().removeCanvas3D( canvas ); } } /** * {@inheritDoc} */ public Canvas3D getCanvas() { if ( firstCanvas == null ) //throw new NullPointerException( "No Canvas3D added to the environment!" ); return ( null ); return ( firstCanvas ); } /** * {@inheritDoc} */ public Canvas3D getCanvas( int index ) { Canvas3D canvas; int k = 0; final int n = getNumberOfViews(); for ( int i = 0; i < n; i++ ) { final View view = getView( i ); final int m = view.numCanvas3Ds(); for ( int j = 0; j < m; j++ ) { canvas = view.getCanvas3D( j ); if ( k == index ) return ( canvas ); k++; } } return ( null ); } /** * {@inheritDoc} */ public void suspendCanvas( Canvas3D canvas ) { if ( canvasEnabledMap.containsKey( canvas ) ) canvasEnabledMap.put( canvas, false ); } /** * {@inheritDoc} */ public void suspendCanvas( Canvas3DWrapper canvasWrapper ) { suspendCanvas( canvasWrapper.getCanvas() ); } /** * {@inheritDoc} */ public void reviveCanvas( Canvas3D canvas ) { if ( !canvasEnabledMap.containsKey( canvas ) ) { addCanvas( canvas ); } else canvasEnabledMap.put( canvas, true ); } /** * {@inheritDoc} */ public void reviveCanvas( Canvas3DWrapper canvasWrapper ) { reviveCanvas( canvasWrapper.getCanvas() ); } /** * {@inheritDoc} */ public boolean isCanvasAlive( Canvas3D canvas ) { if ( canvasEnabledMap.containsKey( canvas ) ) return ( canvasEnabledMap.get( canvas ).booleanValue() ); return ( false ); } /** * {@inheritDoc} */ public boolean isCanvasAlive( Canvas3DWrapper canvasWrapper ) { return ( isCanvasAlive( canvasWrapper.getCanvas() ) ); } /** * Sets a map, that defines a list of RenderPasses to be rendered to each mapped canvas. * The default value is null, which means, that all RenderPasses are rendered to each canvas. * * @param canvasRenderPassMap */ public void setCanvasRenderPassMap( HashMap<Canvas3D, List<RenderPass>> canvasRenderPassMap ) { this.canvasRenderPassMap = canvasRenderPassMap; } /** * {@inheritDoc} */ public void checkRenderPreferences() { final int nv = getNumberOfViews(); for ( int i = 0; i < nv; i++ ) { final View view = getView( i ); final int nc = view.numCanvas3Ds(); for ( int j = 0; j < nc; j++ ) { final Canvas3D canvas = view.getCanvas3D( j ); canvas.getPeer().beforeThreadChanged(); } } } /** * {@inheritDoc} */ public void render( long nanoGameTime, long nanoFrameTime ) { /* * Since a HUD uses its own OperationScheduler now, * we need to update it. */ for ( int i = 0; i < huds.size(); i++ ) { __HUD_PrivilegedAccess.updateOperations( huds.get( i ), nanoGameTime, nanoFrameTime ); } int canvasesCount = 0; long frameId = -1L; SoundProcessor sp = SoundProcessor.getInstance(); List<RenderPass> renderPasses = getRenderer().getRenderPasses(); int numRP = renderPasses.size(); final int nv = getNumberOfViews(); for ( int v = 0; v < nv; v++ ) { final View view = getView( v ); final List< Canvas3D > canvases = view.getCanvas3Ds(); for ( int c = 0; c < canvases.size(); c++ ) { Canvas3D canvas = canvases.get( c ); if ( canvasAliveCheckSuppressed || isCanvasAlive( canvas ) ) { if ( canvasRenderPassMap == null ) frameId = getRenderer().renderOnce( canvas, nanoGameTime, nanoFrameTime ); else frameId = getRenderer().renderOnce( canvasRenderPassMap.get( canvas ), null, canvas, nanoGameTime, nanoFrameTime ); canvasesCount++; } } // Play sounds for ( int i = 0; i < numRP; i++ ) { sp.processAll( renderPasses.get( i ).getBranchGroup(), view, frameId ); } } if ( canvasesCount == 0 ) { System.err.println( "No Canvas3D added to the environment!" ); } } /** * Renderes all Canvas3Ds. * This method is usually called by the RenderLoop thread. * * It simply invokes render( -1L, -1L ). */ public void render() { render( System.nanoTime(), -1L ); } /** * {@inheritDoc} */ public void destroy() { final int nv = getNumberOfViews(); for ( int i = 0; i < nv; i++ ) { final View view = getView( i ); final int nc = view.numCanvas3Ds(); for ( int j = 0; j < nc; j++ ) { final Canvas3D canvas = view.getCanvas3D( j ); canvas.getPeer().destroy(); } } SoundDriver soundDriver = SoundProcessor.getInstance().getSoundDriver(); if ( soundDriver != null ) { soundDriver.shutdown(); } } /** * Creates a new Xith3DEnvironment. * * @param headless true for no View creation * @param eyePositionX the center of the eye * @param eyePositionY the center of the eye * @param eyePositionZ the center of the eye * @param viewFocusX the point the view looks at * @param viewFocusY the point the view looks at * @param viewFocusZ the point the view looks at * @param vecUpX the vector pointing up * @param vecUpY the vector pointing up * @param vecUpZ the vector pointing up * @param renderLoop the RenderLoop instance to link this environment with. */ private Xith3DEnvironment( boolean headless, float eyePositionX, float eyePositionY, float eyePositionZ, float viewFocusX, float viewFocusY, float viewFocusZ, float vecUpX, float vecUpY, float vecUpZ, RenderLoop renderLoop ) { super( new DefaultRenderer() ); // enable layered rendering mode getRenderer().setLayeredMode( true ); // create a view if ( !headless ) { addView( new View( eyePositionX, eyePositionY, eyePositionZ, viewFocusX, viewFocusY, viewFocusZ, vecUpX, vecUpY, vecUpZ ) ); } this.firstCanvas = null; this.canvasEnabledMap = new ConcurrentHashMap<Canvas3D, Boolean>(); if ( renderLoop != null ) { renderLoop.setXith3DEnvironment( this ); } this.renderLoop = renderLoop; this.screenshotEngine = new EnvScreenshotEngineImpl( this ); this.pickScheduler = new PickSchedulerImpl( this ); } /** * Creates a new Xith3DEnvironment. * * @param eyePositionX the center of the eye * @param eyePositionY the center of the eye * @param eyePositionZ the center of the eye * @param viewFocusX the point the view looks at * @param viewFocusY the point the view looks at * @param viewFocusZ the point the view looks at * @param vecUpX the vector pointing up * @param vecUpY the vector pointing up * @param vecUpZ the vector pointing up * @param renderLoop the RenderLoop instance to link this environment with. */ public Xith3DEnvironment( float eyePositionX, float eyePositionY, float eyePositionZ, float viewFocusX, float viewFocusY, float viewFocusZ, float vecUpX, float vecUpY, float vecUpZ, RenderLoop renderLoop ) { this( false, eyePositionX, eyePositionY, eyePositionZ, viewFocusX, viewFocusY, viewFocusZ, vecUpX, vecUpY, vecUpZ, renderLoop ); } /** * Creates a new Xith3DEnvironment. * * @param eyePositionX the center of the eye * @param eyePositionY the center of the eye * @param eyePositionZ the center of the eye * @param viewFocusX the point the view looks at * @param viewFocusY the point the view looks at * @param viewFocusZ the point the view looks at * @param vecUpX the vector pointing up * @param vecUpY the vector pointing up * @param vecUpZ the vector pointing up */ public Xith3DEnvironment( float eyePositionX, float eyePositionY, float eyePositionZ, float viewFocusX, float viewFocusY, float viewFocusZ, float vecUpX, float vecUpY, float vecUpZ ) { this( false, eyePositionX, eyePositionY, eyePositionZ, viewFocusX, viewFocusY, viewFocusZ, vecUpX, vecUpY, vecUpZ, null ); } /** * Creates a new Xith3DEnvironment. * * @param eyePosition the environment's view's location (or <i>null</i> for no View creation) * @param viewFocus the environment's view's center (where to look at) (or <i>null</i> for no View creation) * @param vecUp the environment's view's normal which is pointing up (or <i>null</i> for no View creation) * @param renderLoop the RenderLoop instance to link this environment with. */ public Xith3DEnvironment( Tuple3f eyePosition, Tuple3f viewFocus, Tuple3f vecUp, RenderLoop renderLoop ) { this( (eyePosition == null) || (viewFocus == null) || (vecUp == null), eyePosition != null ? eyePosition.getX() : 0f, eyePosition != null ? eyePosition.getY() : 0f, eyePosition != null ? eyePosition.getZ() : 0f, viewFocus != null ? viewFocus.getX() : 0f, viewFocus != null ? viewFocus.getY() : 0f, viewFocus != null ? viewFocus.getZ() : 0f, vecUp != null ? vecUp.getX() : 0f, vecUp != null ? vecUp.getY() : 0f, vecUp != null ? vecUp.getZ() : 0f, renderLoop ); } /** * Creates a new Xith3DEnvironment. * * @param eyePosition the environment's view's location (or <i>null</i> for no View creation) * @param viewFocus the environment's view's center (where to look at) (or <i>null</i> for no View creation) * @param vecUp the environment's view's normal which is pointing up (or <i>null</i> for no View creation) */ public Xith3DEnvironment( Tuple3f eyePosition, Tuple3f viewFocus, Tuple3f vecUp ) { this( eyePosition, viewFocus, vecUp, null ); } /** * Creates a new Xith3DEnvironment. * * eyePosition is (0, 0, 5) * viewFocus is (0, 0, 0) * vecUp is (0, 1, 0) * * @param renderLoop the RenderLoop instance to link this environment with. */ public Xith3DEnvironment( RenderLoop renderLoop ) { this( 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f, renderLoop ); } /** * Creates a new Xith3DEnvironment. * * eyePosition is (0, 0, 5) * viewFocus is (0, 0, 0) * vecUp is (0, 1, 0) */ public Xith3DEnvironment() { this( 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f ); } /** * Creates a new headless (without a View) Xith3DEnvironment. * * @param renderLoop the RenderLoop instance to link this environment with. */ public static final Xith3DEnvironment createHeadless( RenderLoop renderLoop ) { return ( new Xith3DEnvironment( null, null, null, renderLoop ) ); } /** * Creates a new headless (without a View) Xith3DEnvironment. */ public static final Xith3DEnvironment createHeadless() { return ( new Xith3DEnvironment( null, null, null, null ) ); } }