/* * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.util.ArrayList; import javax.vecmath.Color3f; /** * The Background leaf node defines either a solid background color * or a background image that is used to fill the window at the * beginning of each new frame. It also specifies an application * region in which this background is active. */ class BackgroundRetained extends LeafRetained { static final int COLOR_CHANGED = 0x00001; static final int IMAGE_CHANGED = 0x00002; static final int GEOMETRY_CHANGED = 0x00004; static final int BOUNDS_CHANGED = 0x00008; static final int BOUNDINGLEAF_CHANGED = 0x00010; static final int IMAGE_SCALE_CHANGED = 0x00020; // Background color or image. If non-null, the image overrides the // color. Color3f color = new Color3f(0.0f, 0.0f, 0.0f); ImageComponent2DRetained image = null; Texture2DRetained texture = null; // the image scale mode if image is used. int imageScaleMode = Background.SCALE_NONE; /** * The Boundary object defining the lights's application region. */ Bounds applicationRegion = null; /** * The bounding leaf reference */ BoundingLeafRetained boundingLeaf = null; /** * Background geometry branch group */ BranchGroup geometryBranch = null; /** * The transformed value of the applicationRegion. */ Bounds transformedRegion = null; /** * The state structure used for Background Geometry */ SetLiveState setLiveState = null; /** * The locale of this Background node since we don't have mirror object * when clearLive is called * locale is set to null, we still want locale to have a * non-null value, since renderingEnv structure may be using the * locale */ Locale cachedLocale = null; // This is true when this background is referenced in an immediate mode context boolean inImmCtx = false; // list of light nodes for background geometry ArrayList lights = new ArrayList(); // list of fog nodes for background geometry ArrayList fogs = new ArrayList(); // a list of background geometry atoms ArrayList bgGeometryAtomList = new ArrayList(); // false is background geometry atoms list has changed boolean bgGeometryAtomListDirty = true; // an array of background geometry atoms GeometryAtom[] bgGeometryAtoms = null; // Target threads to be notified when light changes // Note, the rendering env structure only get notified // when there is a bounds related change final static int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT | J3dThread.UPDATE_RENDER; // Is true, if the background is viewScoped boolean isViewScoped = false; BackgroundRetained () { this.nodeType = NodeRetained.BACKGROUND; localBounds = new BoundingBox((Bounds)null); } /** * Initializes the background color to the specified color. * This color is used * if the image is null. * @param color the new background color */ final void initColor(Color3f color) { this.color.set(color); } /** * Sets the background color to the specified color. This color is used * if the image is null. * @param color the new background color */ final void setColor(Color3f color) { initColor(color); if (source.isLive()) { sendMessage(COLOR_CHANGED, new Color3f(color)); } } /** * Initializes the background color to the specified color. * This color is used * if the image is null. * @param r the red component of the background color * @param g the green component of the background color * @param b the blue component of the background color */ final void initColor(float r, float g, float b) { this.color.x = r; this.color.y = g; this.color.z = b; } /** * Sets the background color to the specified color. This color is used * if the image is null. * @param r the red component of the background color * @param g the green component of the background color * @param b the blue component of the background color */ final void setColor(float r, float g, float b) { setColor(new Color3f(r, g, b)); } /** * Retrieves the background color. * @param color the vector that will receive the current background color */ final void getColor(Color3f color) { color.set(this.color); } /** * Initialize the image scale mode to the specified mode * @imageScaleMode the image scale mode to the used */ final void initImageScaleMode(int imageScaleMode){ this.imageScaleMode = imageScaleMode; } /** * Sets the image scale mode for this Background node. * @param imageScaleMode the image scale mode */ final void setImageScaleMode(int imageScaleMode){ initImageScaleMode(imageScaleMode); if(source.isLive()){ sendMessage(IMAGE_SCALE_CHANGED, new Integer(imageScaleMode)); } } /** * gets the image scale mode for this Background node. */ final int getImageScaleMode(){ return imageScaleMode; } /** * Initializes the background image to the specified image. * @param image new ImageCompoent2D object used as the background image */ final void initImage(ImageComponent2D img) { int texFormat; if (img == null) { image = null; texture = null; return; } if (img.retained != image ) { image = (ImageComponent2DRetained) img.retained; image.setEnforceNonPowerOfTwoSupport(true); switch(image.getNumberOfComponents()) { case 1: texFormat = Texture.INTENSITY; break; case 2: texFormat = Texture.LUMINANCE_ALPHA; break; case 3: texFormat = Texture.RGB; break; case 4: texFormat = Texture.RGBA; break; default: assert false; return; } Texture2D tex2D = new Texture2D(Texture.BASE_LEVEL, texFormat, img.getWidth(), img.getHeight()); texture = (Texture2DRetained) tex2D.retained; // Background is special case of Raster. texture.setUseAsRaster(true); // Fix to issue 373 : ImageComponent.set(BufferedImage) ignored when used by Background image.addUser(texture); texture.initImage(0,img); } } /** * Sets the background image to the specified image. * @param image new ImageCompoent3D object used as the background image */ final void setImage(ImageComponent2D img) { if (source.isLive()) { if (texture != null) { texture.clearLive(refCount); } } initImage(img); if (source.isLive()) { if (texture != null) { texture.setLive(inBackgroundGroup, refCount); } sendMessage(IMAGE_CHANGED, (texture != null ? texture.mirror : null)); } } /** * Retrieves the background image. * @return the current background image */ final ImageComponent2D getImage() { return (image == null ? null : (ImageComponent2D)image.source); } /** * Initializes the background geometry branch group to the specified branch. * @param branch new branch group object used for background geometry */ final void initGeometry(BranchGroup branch) { geometryBranch = branch; } /** * Sets the background geometry branch group to the specified branch. * @param branch new branch group object used for background geometry */ final void setGeometry(BranchGroup branch) { int numMessages = 0; int i; if (source.isLive()) { J3dMessage m[]; if (geometryBranch != null) numMessages+=2; // REMOVE_NODES, ORDERED_GROUP_REMOVED if (branch != null) numMessages+=2; // INSERT_NODES, ORDERED_GROUP_INSERTED m = new J3dMessage[numMessages]; for (i=0; i(); setLiveState.branchGroupPaths.add(new BranchGroupRetained[0]); setLiveState.orderedPaths = new ArrayList(1); setLiveState.orderedPaths.add(new OrderedPath()); setLiveState.switchStates = new ArrayList(1); setLiveState.switchStates.add(new SwitchState(false)); branch.setLive(setLiveState); } void clearGeometryBranch(BranchGroupRetained branch) { setLiveState.reset(locale); setLiveState.inBackgroundGroup = true; setLiveState.geometryBackground = this; branch.clearLive(setLiveState); branch.setParent(null); branch.setLocale(null); } /** * This setLive routine first calls the superclass's method, then * it adds itself to the list of lights */ @Override void setLive(SetLiveState s) { super.doSetLive(s); if (inImmCtx) { throw new IllegalSharingException( J3dI18N.getString("BackgroundRetained1")); } if (inBackgroundGroup) { throw new IllegalSceneGraphException(J3dI18N.getString("BackgroundRetained5")); } if (inSharedGroup) { throw new IllegalSharingException(J3dI18N.getString("BackgroundRetained6")); } if (geometryBranch != null) { BranchGroupRetained branch = (BranchGroupRetained)geometryBranch.retained; if (branch.inBackgroundGroup == true) throw new IllegalSharingException( J3dI18N.getString("BackgroundRetained0")); if (branch.parent != null) throw new IllegalSharingException( J3dI18N.getString("BackgroundRetained3")); if (branch.locale != null) throw new IllegalSharingException( J3dI18N.getString("BackgroundRetained4")); if (setLiveState == null) { setLiveState = new SetLiveState(universe); setLiveState.universe = universe; } setGeometryBranch((BranchGroupRetained)geometryBranch.retained); // add background geometry nodes to setLiveState's nodeList s.nodeList.addAll(setLiveState.nodeList); s.notifyThreads |= setLiveState.notifyThreads; s.ogList.addAll(setLiveState.ogList); s.ogChildIdList.addAll(setLiveState.ogChildIdList); s.ogOrderedIdList.addAll(setLiveState.ogOrderedIdList); // Free up memory. setLiveState.reset(null); } if ((s.viewScopedNodeList != null) && (s.viewLists != null)) { s.viewScopedNodeList.add(this); s.scopedNodesViewList.add(s.viewLists.get(0)); } else { s.nodeList.add(this); } // System.err.println("bkg.setlive nodeList " + s.nodeList); // process switch leaf if (s.switchTargets != null && s.switchTargets[0] != null) { s.switchTargets[0].addNode(this, Targets.ENV_TARGETS); } switchState = s.switchStates.get(0); // Initialize some mirror values if (boundingLeaf != null) { transformedRegion = boundingLeaf.mirrorBoundingLeaf.transformedRegion; } else { // Evaluate applicationRegion if not null if (applicationRegion != null) { transformedRegion = (Bounds)applicationRegion.clone(); transformedRegion.transform( applicationRegion, getLastLocalToVworld()); } else { transformedRegion = null; } } cachedLocale = s.locale; // add this node to the transform target if (s.transformTargets != null && s.transformTargets[0] != null) { s.transformTargets[0].addNode(this, Targets.ENV_TARGETS); s.notifyThreads |= J3dThread.UPDATE_TRANSFORM; } s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT| J3dThread.UPDATE_RENDER; if (texture != null) { texture.setLive(inBackgroundGroup, refCount); } super.markAsLive(); } /** * This clearLive routine first calls the superclass's method, then * it removes itself to the list of lights */ @Override void clearLive(SetLiveState s) { super.clearLive(s); if ((s.viewScopedNodeList != null) && (s.viewLists != null)) { s.viewScopedNodeList.add(this); s.scopedNodesViewList.add(s.viewLists.get(0)); } else { s.nodeList.add(this); } if (s.transformTargets != null && s.transformTargets[0] != null) { s.transformTargets[0].addNode(this, Targets.ENV_TARGETS); s.notifyThreads |= J3dThread.UPDATE_TRANSFORM; } if (s.switchTargets != null && s.switchTargets[0] != null) { s.switchTargets[0].addNode(this, Targets.ENV_TARGETS); } if (geometryBranch != null) { BranchGroupRetained branch = (BranchGroupRetained)geometryBranch.retained; clearGeometryBranch(branch); // add background geometry nodes to setLiveState's nodeList s.nodeList.addAll(setLiveState.nodeList); s.ogList.addAll(setLiveState.ogList); s.ogChildIdList.addAll(setLiveState.ogChildIdList); s.notifyThreads |= setLiveState.notifyThreads; // Free up memory. setLiveState.reset(null); lights.clear(); fogs.clear(); } if (texture != null) { texture.clearLive(refCount); } s.notifyThreads |= J3dThread.UPDATE_RENDERING_ENVIRONMENT| J3dThread.UPDATE_RENDER; } // The update Object function. synchronized void updateImmediateMirrorObject(Object[] objs) { int component = ((Integer)objs[1]).intValue(); // If initialization // Bounds message only sent when boundingleaf is null if ((component & BOUNDS_CHANGED) != 0) { if (objs[2] != null) { transformedRegion = ((Bounds) objs[2]).copy(transformedRegion); transformedRegion.transform( (Bounds) objs[2], getCurrentLocalToVworld()); } else { transformedRegion = null; } } else if ((component & BOUNDINGLEAF_CHANGED) != 0) { if (objs[2] != null) { transformedRegion = ((BoundingLeafRetained)objs[2]).transformedRegion; } else { // Evaluate applicationRegion if not null Bounds appRegion = (Bounds)objs[3]; if (appRegion != null) { transformedRegion = appRegion.copy(transformedRegion); transformedRegion.transform( appRegion, getCurrentLocalToVworld()); } else { transformedRegion = null; } } } } /** Note: This routine will only be called * to update the object's * transformed region */ @Override void updateBoundingLeaf() { if (boundingLeaf != null && boundingLeaf.mirrorBoundingLeaf.switchState.currentSwitchOn) { transformedRegion = boundingLeaf.mirrorBoundingLeaf.transformedRegion; } else { // Evaluate applicationRegion if not null if (applicationRegion != null) { transformedRegion = applicationRegion.copy(transformedRegion); transformedRegion.transform( applicationRegion, getCurrentLocalToVworld()); } else { transformedRegion = null; } } } void updateImmediateTransformChange() { // If bounding leaf is null, tranform the bounds object if (boundingLeaf == null) { if (applicationRegion != null) { transformedRegion = applicationRegion.copy(transformedRegion); transformedRegion.transform( applicationRegion, getCurrentLocalToVworld()); } } } final void sendMessage(int attrMask, Object attr) { J3dMessage createMessage = new J3dMessage(); createMessage.threads = targetThreads; createMessage.universe = universe; createMessage.type = J3dMessage.BACKGROUND_CHANGED; createMessage.args[0] = this; createMessage.args[1]= new Integer(attrMask); createMessage.args[2] = attr; VirtualUniverse.mc.processMessage(createMessage); } void addBgGeometryAtomList(GeometryAtom geomAtom) { bgGeometryAtomList.add(geomAtom); bgGeometryAtomListDirty = true; } void removeBgGeometryAtomList(GeometryAtom geomAtom) { bgGeometryAtomList.remove(geomAtom); bgGeometryAtomListDirty = true; } GeometryAtom[] getBackgroundGeometryAtoms() { if (bgGeometryAtomListDirty) { int nAtoms = bgGeometryAtomList.size(); if (nAtoms == 0) { bgGeometryAtoms = null; } else { bgGeometryAtoms = bgGeometryAtomList.toArray(new GeometryAtom[nAtoms]); bgGeometryAtomListDirty = false; } } return bgGeometryAtoms; } @Override void mergeTransform(TransformGroupRetained xform) { super.mergeTransform(xform); if (applicationRegion != null) { applicationRegion.transform(xform.transform); } } // notifies the Background object that the image data in a referenced // ImageComponent object is changed. // Currently we are not making use of this information. void notifyImageComponentImageChanged(ImageComponentRetained image, ImageComponentUpdateInfo value) { } @Override void getMirrorObjects(ArrayList leafList, HashKey key) { leafList.add(this); // No Mirror in this case } }