/** * 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.scenegraph; import java.util.ArrayList; import java.util.List; import org.jagatoo.input.InputSystem; import org.xith3d.render.BackgroundRenderPass; import org.xith3d.render.BaseRenderPassConfig; import org.xith3d.render.Canvas3D; import org.xith3d.render.ForegroundRenderPass; import org.xith3d.render.RenderPass; import org.xith3d.render.RenderPassConfig; import org.xith3d.render.Renderer; import org.xith3d.render._RNDR_PrivilegedAccess; import org.xith3d.scenegraph.View.ProjectionPolicy; import org.xith3d.scenegraph.modifications.ScenegraphModificationsListener; import org.xith3d.ui.hud.HUD; /** * A SceneGraph provides all methods to control adding and removing Nodes of * all kinds and adding and removing of RenderPasses. * It also provides getters for the Root objects of a Scenegraph. * * @see org.xith3d.base.Xith3DEnvironment * * @author Marvin Froehlich (aka Qudus) */ public class SceneGraph { /** * If true, the renderer checks for illegal nodes modification * * @see #checkForIllegalModification(Node) */ protected static final boolean CHECK_FOR_ILLEGAL_MODIFICATION = false; private final Renderer renderer; private final ArrayList< View > views = new ArrayList< View >(); protected final ArrayList< HUD > huds = new ArrayList< HUD >(); /** * The list of BranchGroups contained in this SceneGraph. */ private final ArrayList< BranchGroup > branchGroups = new ArrayList< BranchGroup >(); public ScenegraphModificationsListener modListener = null; /** * can be used by nodes to make sure that a change is not being attempted at runtime. */ protected static final void checkForIllegalModification( final Node node ) { if ( node.isLive() && _RNDR_PrivilegedAccess.getRenderersWorking() > 0 ) { new Error( "Illegal Scenegraph Modification " ).printStackTrace(); System.exit( 100 ); } } /** * @return this SceneGraph's Renderer. */ public final Renderer getRenderer() { return ( renderer ); } /** * Adds a new ScenegraphModificationListener to the List. * It will be notified of any scenegraph change at runtime. * * @param modListener the new ScenegraphModificationsListener to add */ public final void addScenegraphModificationListener( ScenegraphModificationsListener modListener ) { getRenderer().addScenegraphModificationListener( modListener ); } /** * Removes a ScenegraphModificationListener from the List. * * @param modListener the ScenegraphModificationsListener to be removed */ public final void removeScenegraphModificationListener( ScenegraphModificationsListener modListener ) { getRenderer().removeScenegraphModificationListener( modListener ); } /** * Adds a new View to the SceneGraph. * * @param view the View to be added */ public final void addView( View view ) { views.add( view ); } /** * @return this SceneGraph's Views count. */ public final int getNumberOfViews() { return ( views.size() ); } /** * @return this SceneGraph's (first) View or null, if no View is present. */ public final View getView() { if ( views.size() == 0 ) return ( null ); return ( views.get( 0 ) ); } /** * @return this SceneGraph's View with the specified index. * * @param index the desired View's index in the SceneGraph */ public View getView( int index ) { return ( views.get( index ) ); } private final void addBranchGroup( BranchGroup bg ) { branchGroups.add( bg ); bg.setSceneGraph( this ); if ( modListener != null ) modListener.onBranchGraphAdded( bg ); } /** * Adds a new BranchGroup to the SceneGraph's Locale.<br> * The also created and added RenderPass is automatically linked with the BranchGroup.<br> * <br> * There're convenience methods, with which you don't need to pass a * RenderPassConfigProvider.<br> * <br> * @see org.xith3d.scenegraph.BranchGroup * @see RenderPassConfig * @see BaseRenderPassConfig * @see #addParallelBranch( BranchGroup ) * @see #addPerspectiveBranch( BranchGroup ) * * @param branchGraph the new branch graph to add * @param renderPassConfig the configuration for the new RenderPass to add * * @return the created RenderPass */ public final RenderPass addBranchGraph( BranchGroup branchGraph, RenderPassConfig renderPassConfig ) { addBranchGroup( branchGraph ); ProjectionPolicy projPoli = renderPassConfig.getProjectionPolicy(); if ( ( projPoli == null ) && ( getView() != null ) ) projPoli = getView().getProjectionPolicy(); RenderPass renderPass = new RenderPass( branchGraph, renderPassConfig ); addRenderPass( renderPass ); return ( renderPass ); } /** * Adds a perspective (projected) RenderPass and links it to the given BranchGroup.<br> * <br> * This is a convenience method and is functionally equal to:<br> * <blockquote> * addBranchGraph( branchGraph, new RenderPassConfig( RenderPassConfigProvider.PERSPECTIVE_PROJECTION ) );<br> * </blockquote> * * @see org.xith3d.scenegraph.BranchGroup * @see #addParallelBranch( BranchGroup ) * * @param branchGraph the BranchGroup used for the new RenderPass * @return the created RenderPass */ public final RenderPass addPerspectiveBranch( BranchGroup branchGraph ) { RenderPassConfig persPassConfig = new BaseRenderPassConfig( ProjectionPolicy.PERSPECTIVE_PROJECTION ); return ( addBranchGraph( branchGraph, persPassConfig ) ); } /** * Adds a perspective (projected) RenderPass and links it to a new BranchGroup, * which also been added to the Locale.<br> * <br> * This is a convenience method and is functionally equal to:<br> * <blockquote> * addBranchGraph( new BranchGroup(), new RenderPassConfig( RenderPassConfigProvider.PERSPECTIVE_PROJECTION ) );<br> * or<br> * addPerspectiveBranch( new BranchGroup() );<br> * </blockquote> * * @see #addPerspectiveBranch( BranchGroup ) * @see #addParallelBranch( BranchGroup ) * * @return the created RenderPass */ public final RenderPass addPerspectiveBranch() { return ( addPerspectiveBranch( new BranchGroup() ) ); } /** * Adds a parallel (projected) RenderPass and links it to the given BranchGroup.<br> * <br> * This is a convenience method and is functionally equal to:<br> * <blockquote> * addBranchGraph( branchGraph, new RenderPassConfig( RenderPassConfigProvider.PARALLEL_PROJECTION ) );<br> * </blockquote> * * @see org.xith3d.scenegraph.BranchGroup * @see #addPerspectiveBranch( BranchGroup ) * * @param branchGraph the BranchGroup used for the new RenderPass * @return the created RenderPass */ public final RenderPass addParallelBranch( BranchGroup branchGraph ) { RenderPassConfig paraPassConfig = new BaseRenderPassConfig( ProjectionPolicy.PARALLEL_PROJECTION ); return ( addBranchGraph( branchGraph, paraPassConfig ) ); } /** * Adds a parallel (projected) RenderPass and links it to a new BranchGroup, * which is also been added to the Locale.<br> * <br> * This is a convenience method and is functionally equal to:<br> * <blockquote> * addBranchGraph( branchGraph, new RenderPassConfig( RenderPassConfigProvider.PARALLEL_PROJECTION ) );<br> * or<br> * addParallelBranch( new BranchGroup() );<br> * </blockquote> * * @see #addParallelBranch( BranchGroup ) * @see #addPerspectiveBranch( BranchGroup ) * * @return the created RenderPass */ public final RenderPass addParallelBranch() { return ( addParallelBranch( new BranchGroup() ) ); } /** * Removes the given BranchGroup from the SceneGraph.<br> * The assotiated RenderPass is also removed from the Renderer. * * @param branchGraph the BranchGroup to remove */ public final void removeBranchGraph( BranchGroup branchGraph ) { final List< RenderPass > renderPasses = getRenderer().getRenderPasses( branchGraph ); branchGraph.setSceneGraph( null ); if ( modListener != null ) modListener.onBranchGraphRemoved( branchGraph ); for ( int i = 0; i < renderPasses.size(); i++ ) _RNDR_PrivilegedAccess.removeRenderPass( renderPasses.get( i ), getRenderer() ); branchGroups.remove( branchGraph ); } /** * @return the number of BranchGroups in this SceneGraph. */ public final int getNumberOfBranchGroups() { return ( branchGroups.size() ); } /** * @return the total number of children in this group and its subgroups. */ public final long getTotalNumChildren() { long totalNumChildren = 0L; for ( int i = 0; i < branchGroups.size(); i++ ) { totalNumChildren += 1 + branchGroups.get( i ).getTotalNumChildren(); } return ( totalNumChildren ); } /** * @return the total number of shapes in this group and its subgroups. */ public final long getTotalNumShapes() { long totalNumShapes = 0L; for ( int i = 0; i < branchGroups.size(); i++ ) { totalNumShapes += branchGroups.get( i ).getTotalNumShapes(); } return ( totalNumShapes ); } /** * @return the n-th BranchGroup in this SceneGraph. */ public final BranchGroup getBranchGroup( int index ) { return ( branchGroups.get( index ) ); } /** * @return the <b>first</b> BranchGroup in this SceneGraph or <i>null</i>. */ public final BranchGroup getBranchGroup() { if ( branchGroups.size() == 0 ) return ( null ); return ( getBranchGroup( 0 ) ); } private final int findLastBackgroundPass() { final List< RenderPass > renderPasses = getRenderer().getRenderPasses(); for ( int i = 0; i < renderPasses.size(); i++ ) { final RenderPass rp = renderPasses.get( i ); if ( !( rp instanceof BackgroundRenderPass ) ) return ( i - 1 ); } return ( renderPasses.size() - 1 ); } private final int findLastNormalPass() { final int lastBackgroundPass = findLastBackgroundPass(); final List< RenderPass > renderPasses = getRenderer().getRenderPasses(); for ( int i = lastBackgroundPass + 1; i < renderPasses.size(); i++ ) { final RenderPass rp = renderPasses.get( i ); if ( rp instanceof ForegroundRenderPass ) return ( i - 1 ); } //if ( renderPasses.size() > lastBackgroundPass + 1 ) return ( renderPasses.size() - 1 ); //else // return ( -1 ); } /** * Removes the given RenderPass from the SceneGraph's Renderer.<br> * The assotiated BranchGroup is also removed. * * @param renderPass the RenderPass to remove */ public final void removeRenderPass( RenderPass renderPass ) { removeBranchGraph( renderPass.getBranchGroup() ); } /** * Adds a RenderPass to the SceneGraph's Renderer.<br> * The BranchGroup assotiated to the RenderPass is also added to the * SceneGraph. * * @param renderPass the new RenderPass to add * * @return the RenderPass'es BranchGroup to add further children to */ public final BranchGroup addRenderPass( RenderPass renderPass ) { if ( !branchGroups.contains( renderPass.getBranchGroup() ) ) { addBranchGroup( renderPass.getBranchGroup() ); } if ( getRenderer().getRenderPasses().contains( renderPass ) ) return ( renderPass.getBranchGroup() ); //System.out.println( renderPass + ", " + findLastBackgroundPass() ); if ( renderPass instanceof BackgroundRenderPass ) { _RNDR_PrivilegedAccess.addRenderPass( findLastBackgroundPass() + 1, renderPass, getRenderer() ); } else if ( renderPass instanceof ForegroundRenderPass ) { _RNDR_PrivilegedAccess.addRenderPass( renderPass, getRenderer() ); } else { _RNDR_PrivilegedAccess.addRenderPass( findLastNormalPass() + 1, renderPass, getRenderer() ); } /* int i = 0; for ( RenderPass rp: getRenderer().getRenderPasses() ) { System.out.println( i++ + ", " + rp ); } */ return ( renderPass.getBranchGroup() ); } /** * Adds a RenderPass to the SceneGraph at first position.<br> * The BranchGroup assotiated to the RenderPass is also added to the * SceneGraph. * * @param renderPass the new RenderPass to add * * @return the RenderPass'es BranchGroup to add further children to */ public final BranchGroup addRenderPassFirst( RenderPass renderPass ) { if ( !branchGroups.contains( renderPass.getBranchGroup() ) ) { addBranchGroup( renderPass.getBranchGroup() ); } _RNDR_PrivilegedAccess.addRenderPass( 0, renderPass, getRenderer() ); return ( renderPass.getBranchGroup() ); } /** * Removes all children from the SceneGraph. */ public final void removeAllBranchGraphs() { if ( modListener != null ) { for ( int i = 0; i < branchGroups.size(); i++ ) { modListener.onBranchGraphRemoved( branchGroups.get( i ) ); } } branchGroups.clear(); _RNDR_PrivilegedAccess.removeAllRenderPasses( getRenderer() ); } private final Canvas3D findFirstCanvas() { final int nv = getNumberOfViews(); for ( int i = 0; i < nv; i++ ) { final View view = getView( i ); if ( view.numCanvas3Ds() > 0 ) return ( view.getCanvas3D( 0 ) ); } return ( null ); } /** * Adds a HUD to the SceneGraph and sets all necessary properties. * * @param hud the HUD to be added to the SceneGraph */ public final RenderPass addHUD( HUD hud ) { final RenderPass renderPass = hud.getRenderPass(); addRenderPass( renderPass ); huds.add( hud ); final Canvas3D canvas = findFirstCanvas(); if ( canvas != null ) { hud.connect( canvas ); } if ( InputSystem.hasInstance() ) { hud.connect( InputSystem.getInstance() ); } return ( renderPass ); } /** * Returns the HUD (first) attached to the SceneGraph id any. * * @return the (first) hud or null */ public final HUD getHUD() { return ( huds.isEmpty() ? null : huds.get( 0 ) ); } /** * Removes a HUD from the SceneGraph. * * @param hud the HUD to be removed from the SceneGraph * @param inputMgr the InputManager to get input events from */ public final void removeHUD( HUD hud ) { removeRenderPass( hud.getRenderPass() ); huds.remove( hud ); final Canvas3D canvas = findFirstCanvas(); if ( canvas != null ) { hud.disconnect( canvas ); } if ( InputSystem.hasInstance() ) { hud.disconnect( InputSystem.getInstance() ); } } public SceneGraph( Renderer renderer ) { this.renderer = renderer; } }