/* * Copyright 1996-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 java.util.Enumeration; import java.util.Vector; import javax.vecmath.Color3f; /** * LightRetained is an abstract class that contains instance variable common to * all lights. */ abstract class LightRetained extends LeafRetained { // Statics used when something in the light changes static final int ENABLE_CHANGED = 0x0001; static final int SCOPE_CHANGED = 0x0002; static final int BOUNDS_CHANGED = 0x0004; static final int COLOR_CHANGED = 0x0008; static final int BOUNDINGLEAF_CHANGED = 0x0010; static final int INIT_MIRROR = 0x0020; static final int CLEAR_MIRROR = 0x0040; static final int LAST_DEFINED_BIT = 0x0040; // Indicates whether the light is turned on. boolean lightOn = true; // The color of the light (white by default). Color3f color = new Color3f(1.0f, 1.0f, 1.0f); // This node which specifies the hierarchical scope of the // light. A null reference means that this light has universal // scope. Vector scopes = new Vector(); /** * The Boundary object defining the lights's region of influence. */ Bounds regionOfInfluence = null; /** * The bounding leaf reference */ BoundingLeafRetained boundingLeaf = null; /** * The transformed value of the applicationRegion. */ Bounds region = null; /** * This bitmask is set when something changes in the light */ int lightDirty = 0xffff; // This is a copy of the sgLight's dirty bits int sgLightDirty = 0xffff; // The type of light int lightType = -1; // This is true when this light is needed in the current light set boolean isNeeded = false; // This is true when this light is referenced in an immediate mode context boolean inImmCtx = false; // A back reference to the scene graph light, when this is a mirror light LightRetained sgLight = null; // A HashKey for lights in a shared group HashKey key = null; // An array of mirror lights, one for each instance of this light in a // shared group. Entry 0 is the only one valid if we are not in a shared // group. LightRetained[] mirrorLights = new LightRetained[1]; // The number of valid lights in mirrorLights int numMirrorLights = 0; // Indicated whether the light is a scoped light boolean isScoped = false; // The object that contains the dynamic HashKey - a string type object // Used in scoping HashKey tempKey = new HashKey(250); /** * A list of all the EnvironmentSets that reference this light. * Note that multiple RenderBin update thread may access * this shared environmentSets simultaneously. * So we use UnorderList when sync. all the operations. */ UnorderList environmentSets = new UnorderList(1, EnvironmentSet.class); // Is true, if the mirror light is viewScoped boolean isViewScoped = false; /** * Temporary list of newly added mirror lights, during any setlive */ ArrayList newlyAddedMirrorLights = new ArrayList(); // Target threads to be notified when light changes static final int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT | J3dThread.UPDATE_RENDER; /** * Initialize the light on or off. * @param state true or false to enable or disable the light */ void initEnable(boolean state) { this.lightOn = state; } /** * Turns the light on or off and send a message * @param state true or false to enable or disable the light */ void setEnable(boolean state) { initEnable(state); sendMessage(ENABLE_CHANGED, (state ? Boolean.TRUE: Boolean.FALSE)); } /** * Returns the state of the light (on/off). * @return true if the light is on, false if the light is off. */ boolean getEnable() { return this.lightOn; } /** * Initialize the color of this light node. * @param color the value of this new light color */ void initColor(Color3f color) { this.color.set(color); } /** * Sets the color of this light node and send a message * @param color the value of this new light color */ void setColor(Color3f color) { initColor(color); sendMessage(COLOR_CHANGED, new Color3f(color)); } /** * Retrieves the color of this light. * @param color the vector that will receive the color of this light */ void getColor(Color3f color) { color.set(this.color); } /** * Initializes the specified scope with the scope provided. * @param scope the new scope * @param index which scope to replace */ void initScope(Group scope, int index) { GroupRetained group = (GroupRetained)scope.retained; scopes.setElementAt(group, index); } /** * Replaces the specified scope with the scope provided and * send a message * @param scope the new scope * @param index which scope to replace */ void setScope(Group scope, int index) { ArrayList addScopeList = new ArrayList(); ArrayList removeScopeList = new ArrayList(); Object[] scopeInfo = new Object[3]; GroupRetained group = scopes.get(index); tempKey.reset(); group.removeAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights, removeScopeList, tempKey); group = (GroupRetained)scope.retained; tempKey.reset(); // If its a group, then add the scope to the group, if // its a shape, then keep a list to be added during // updateMirrorObject group.addAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights,addScopeList, tempKey); initScope(scope, index); scopeInfo[0] = addScopeList; scopeInfo[1] = removeScopeList; scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE); sendMessage(SCOPE_CHANGED, scopeInfo); } /** * Inserts the specified scope at specified index. * @param scope the new scope * @param index position to insert new scope at */ void initInsertScope(Group scope, int index) { GroupRetained group = (GroupRetained)scope.retained; scopes.insertElementAt(group, index); group.setLightScope(); } /** * Inserts the specified scope at specified index. * @param scope the new scope * @param index position to insert new scope at */ void insertScope(Group scope, int index) { Object[] scopeInfo = new Object[3]; ArrayList addScopeList = new ArrayList(); GroupRetained group = (GroupRetained)scope.retained; tempKey.reset(); group.addAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights,addScopeList, tempKey); initInsertScope(scope, index); scopeInfo[0] = addScopeList; scopeInfo[1] = null; scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE); sendMessage(SCOPE_CHANGED, scopeInfo); } /** * Removes the scope at specified index. * @param index which scope to remove */ void initRemoveScope(int index) { GroupRetained group = scopes.remove(index); group.removeLightScope(); } /** * Removes the scope at specified index. * @param index which scope to remove */ void removeScope(int index) { Object[] scopeInfo = new Object[3]; ArrayList removeScopeList = new ArrayList(); GroupRetained group = scopes.elementAt(index); tempKey.reset(); group.removeAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights, removeScopeList, tempKey); initRemoveScope(index); scopeInfo[0] = null; scopeInfo[1] = removeScopeList; scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE); sendMessage(SCOPE_CHANGED, scopeInfo); } /** * Removes the specified scope * @param scope to be removed */ void removeScope(Group scope) { int ind = indexOfScope(scope); if(ind >= 0) removeScope(ind); } void initRemoveScope(Group scope) { int ind = indexOfScope(scope); if(ind >= 0) initRemoveScope(ind); } /** * Removes all the scopes from this Light's list of scopes */ void removeAllScopes() { int n = scopes.size(); Object[] scopeInfo = new Object[3]; ArrayList removeScopeList = new ArrayList(); for(int index = n-1; index >= 0; index--) { GroupRetained group = scopes.elementAt(index); tempKey.reset(); group.removeAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights, removeScopeList, tempKey); initRemoveScope(index); } scopeInfo[0] = null; scopeInfo[1] = removeScopeList; scopeInfo[2] = (Boolean.FALSE); sendMessage(SCOPE_CHANGED, scopeInfo); } void initRemoveAllScopes() { int n = scopes.size(); for(int i = n-1; i >= 0; i--) initRemoveScope(i); } /** * Returns the scope specified by the index. * @param index of the scope to be returned * @return the scope at location index */ Group getScope(int index) { return (Group)scopes.elementAt(index).source; } /** * Returns an enumeration object of the scope * @return an enumeration object of the scope */ Enumeration getAllScopes() { Enumeration elm = scopes.elements(); Vector v = new Vector(scopes.size()); while (elm.hasMoreElements()) { v.add((Group)elm.nextElement().source); } return v.elements(); } /** * Appends the specified scope to this node's list of scopes. * @param scope the scope to add to this node's list of scopes */ void initAddScope(Group scope) { GroupRetained group = (GroupRetained)scope.retained; scopes.addElement(group); group.setLightScope(); } /** * Appends the specified scope to this node's list of scopes. * @param scope the scope to add to this node's list of scopes */ void addScope(Group scope) { Object[] scopeInfo = new Object[3]; ArrayList addScopeList = new ArrayList(); GroupRetained group = (GroupRetained)scope.retained; initAddScope(scope); tempKey.reset(); group.addAllNodesForScopedLight((inSharedGroup?numMirrorLights:1), mirrorLights,addScopeList, tempKey); scopeInfo[0] = addScopeList; scopeInfo[1] = null; scopeInfo[2] = (scopes.size() > 0 ? Boolean.TRUE: Boolean.FALSE); sendMessage(SCOPE_CHANGED, scopeInfo); } /** * Returns a count of this nodes' scopes. * @return the number of scopes descendant from this node */ int numScopes() { return scopes.size(); } /** * Returns the index of the specified scope * @return index of the scope in this Light's list of scopes */ int indexOfScope(Group scope) { if(scope != null) return scopes.indexOf(scope.retained); else return scopes.indexOf(null); } /** * Initializes the Light's region of influence. * @param region a region that contains the Light's new region of influence */ void initInfluencingBounds(Bounds region) { if (region != null) { regionOfInfluence = (Bounds) region.clone(); if (staticTransform != null) { regionOfInfluence.transform(staticTransform.transform); } } else { regionOfInfluence = null; } } /** * Set the Light's region of influence and send a message * @param region a region that contains the Light's new region of influence */ void setInfluencingBounds(Bounds region) { initInfluencingBounds(region); sendMessage(BOUNDS_CHANGED, (region != null ? region.clone() : null)); } /** * Get the Light's region of influence * @return this Light's region of influence information */ Bounds getInfluencingBounds() { Bounds b = null; if (regionOfInfluence != null) { b = (Bounds) regionOfInfluence.clone(); if (staticTransform != null) { Transform3D invTransform = staticTransform.getInvTransform(); b.transform(invTransform); } } return b; } /** * Initializes the Light's region of influence to the specified Leaf node. */ void initInfluencingBoundingLeaf(BoundingLeaf region) { if (region != null) { boundingLeaf = (BoundingLeafRetained)region.retained; } else { boundingLeaf = null; } } /** * Set the Light's region of influence to the specified Leaf node. */ void setInfluencingBoundingLeaf(BoundingLeaf region) { int i, numLgts; numLgts = numMirrorLights; if (numMirrorLights == 0) numLgts = 1; if (boundingLeaf != null) { // Remove the mirror lights as users of the original bounding leaf for (i = 0; i < numLgts; i++) { boundingLeaf.mirrorBoundingLeaf.removeUser(mirrorLights[i]); } } if (region != null) { boundingLeaf = (BoundingLeafRetained)region.retained; // Add all mirror lights as user of this bounding leaf for (i = 0; i < numLgts; i++) { boundingLeaf.mirrorBoundingLeaf.addUser(mirrorLights[i]); } } else { boundingLeaf = null; } sendMessage(BOUNDINGLEAF_CHANGED, (boundingLeaf != null ? boundingLeaf.mirrorBoundingLeaf : null)); } /** * Get the Light's region of influence. */ BoundingLeaf getInfluencingBoundingLeaf() { return (boundingLeaf != null ? (BoundingLeaf)boundingLeaf.source : null); } /** * This sets the immedate mode context flag */ void setInImmCtx(boolean inCtx) { inImmCtx = inCtx; } /** * This gets the immedate mode context flag */ boolean getInImmCtx() { return (inImmCtx); } // Called on the parent Light object and loops over the mirror object void initMirrorObject(Object[] args) { Shape3DRetained shape; Object[] scopeInfo = (Object[])((Object[])args[4])[5]; ArrayList gAtomList = (ArrayList)scopeInfo[1]; Boolean scoped = (Boolean)scopeInfo[0]; BoundingLeafRetained bl=(BoundingLeafRetained)((Object[])args[4])[0]; Bounds bnds = (Bounds)((Object[])args[4])[1]; int numLgts = ((Integer)args[2]).intValue(); LightRetained[] mLgts = (LightRetained[]) args[3]; int k; for ( k = 0; k < numLgts; k++) { for (int i = 0; i < gAtomList.size(); i++) { shape = ((GeometryAtom)gAtomList.get(i)).source; shape.addLight(mLgts[k]); } mLgts[k].isScoped = scoped.booleanValue(); } for (k = 0; k < numLgts; k++) { mLgts[k].inBackgroundGroup = ((Boolean)((Object[])args[4])[2]).booleanValue(); mLgts[k].geometryBackground = (BackgroundRetained)((Object[])args[4])[3]; if (bl != null) { mLgts[k].boundingLeaf = bl.mirrorBoundingLeaf; mLgts[k].region = mLgts[k].boundingLeaf.transformedRegion; } else { mLgts[k].boundingLeaf = null; mLgts[k].region = null; } if (bnds != null) { mLgts[k].regionOfInfluence = bnds; if (mLgts[k].region == null) { mLgts[k].region = (Bounds)regionOfInfluence.clone(); mLgts[k].region.transform(regionOfInfluence, getLastLocalToVworld()); } } else { mLgts[k].regionOfInfluence = null; } mLgts[k].lightOn = ((Boolean)((Object[])args[4])[4]).booleanValue(); } // if its a ambient light,then do a immediate update of color if (this instanceof AmbientLightRetained) { Color3f clr = (Color3f) ((Object[])args[4])[6]; for (int i = 0; i < numLgts; i++) { mLgts[i].color.set(clr); } } } /** * This method is implemented by each light for rendering * context updates. This default one does nothing. */ abstract void update(Context ctx, int lightSlot, double scale); // This routine is called when rendering Env structure // get a message, this routine updates values in the mirror object // that are not used by the renderer void updateImmediateMirrorObject(Object[] objs) { int component = ((Integer)objs[1]).intValue(); int numLgts = ((Integer)objs[2]).intValue(); LightRetained[] mLgts = (LightRetained[]) objs[3]; // Color changed called immediately only for ambient lights if ((component & COLOR_CHANGED) != 0) { for (int i = 0; i < numLgts; i++) { mLgts[i].color.set(((Color3f)objs[4])); } } else if ((component & ENABLE_CHANGED) != 0) { for (int i = 0; i < numLgts; i++) mLgts[i].lightOn = ((Boolean)objs[4]).booleanValue(); } else if ((component & BOUNDS_CHANGED) != 0) { for (int i = 0; i < numLgts; i++) { mLgts[i].regionOfInfluence = (Bounds) objs[4]; if (mLgts[i].boundingLeaf == null) { if (objs[4] != null) { mLgts[i].region = mLgts[i].regionOfInfluence.copy(mLgts[i].region); mLgts[i].region.transform(mLgts[i].regionOfInfluence, mLgts[i].getCurrentLocalToVworld()); } else { mLgts[i].region = null; } } } } else if ((component & BOUNDINGLEAF_CHANGED) != 0) { for (int i = 0; i < numLgts; i++) { mLgts[i].boundingLeaf=((BoundingLeafRetained)objs[4]); if (objs[4] != null) { mLgts[i].region = mLgts[i].boundingLeaf.transformedRegion; } else { // evaluate regionOfInfluence if not null if (mLgts[i].regionOfInfluence != null) { mLgts[i].region = mLgts[i].regionOfInfluence.copy(mLgts[i].region); mLgts[i].region.transform(mLgts[i].regionOfInfluence, mLgts[i].getCurrentLocalToVworld()); } else { mLgts[i].region = null; } } } } else if ((component & SCOPE_CHANGED) != 0) { int j, i; Object[] scopeList = (Object[])objs[4]; ArrayList addList = (ArrayList)scopeList[0]; ArrayList removeList = (ArrayList)scopeList[1]; boolean isScoped = ((Boolean)scopeList[2]).booleanValue(); if (addList != null) { for (i = 0; i < numLgts; i++) { mLgts[i].isScoped = isScoped; for (j = 0; j < addList.size(); j++) { Shape3DRetained obj = ((GeometryAtom)addList.get(j)).source; obj.addLight(mLgts[i]); } } } if (removeList != null) { for (i = 0; i < numLgts; i++) { mLgts[i].isScoped = isScoped; for (j = 0; j < removeList.size(); j++) { Shape3DRetained obj = ((GeometryAtom)removeList.get(j)).source; obj.removeLight(mLgts[i]); } } } } } // The update Object function called during RenderingEnv objUpdate // Note : if you add any more fields here , you need to update // updateLight() in RenderingEnvironmentStructure @Override void updateMirrorObject(Object[] objs) { int component = ((Integer)objs[1]).intValue(); int numLgts = ((Integer)objs[2]).intValue(); LightRetained[] mLgts = (LightRetained[]) objs[3]; if ((component & COLOR_CHANGED) != 0) { for (int i = 0; i < numLgts; i++) { mLgts[i].color.set(((Color3f)objs[4])); } } if ((component & INIT_MIRROR) != 0) { for (int i = 0; i < numLgts; i++) { Color3f clr = (Color3f) ((Object[])objs[4])[6]; mLgts[i].color.set(clr); } } } /** Note: This routine will only be called on * the mirror object - will update the object's * cached region and transformed region */ @Override void updateBoundingLeaf() { // This is necessary, if for example, the region // changes from sphere to box. if (boundingLeaf != null && boundingLeaf.switchState.currentSwitchOn) { region = boundingLeaf.transformedRegion; } else { // evaluate regionOfInfluence if not null if (regionOfInfluence != null) { region = regionOfInfluence.copy(region); region.transform(regionOfInfluence, getCurrentLocalToVworld()); } else { region = null; } } } @Override void getMirrorObjects(ArrayList leafList, HashKey key) { if (!inSharedGroup) { leafList.add(mirrorLights[0]); } else { for (int i=0; i 0) { J3dMessage createMessage = new J3dMessage(); LightRetained[] mlts = new LightRetained[newlyAddedMirrorLights.size()]; for (int i = 0; i < mlts.length; i++) { mlts[i] = newlyAddedMirrorLights.get(i); } createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT; createMessage.universe = universe; createMessage.type = J3dMessage.LIGHT_CHANGED; createMessage.args[0] = this; createMessage.args[1]= new Integer(CLEAR_MIRROR); ArrayList removeScopeList = new ArrayList(); for (int i = 0; i < scopes.size(); i++) { GroupRetained group = scopes.get(i); tempKey.reset(); group.removeAllNodesForScopedLight(mlts.length, mlts, removeScopeList, tempKey); } createMessage.args[2] = removeScopeList; createMessage.args[3] = new Integer(mlts.length); createMessage.args[4] = mlts; VirtualUniverse.mc.processMessage(createMessage); } } void clearMirrorObject(Object[] args) { Shape3DRetained shape; ArrayList shapeList = (ArrayList)args[2]; LightRetained[] mLgts = (LightRetained[]) args[4]; int numLgts = ((Integer)args[3]).intValue(); for (int k = 0; k < numLgts; k++) { for (int i = 0; i < shapeList.size(); i++) { shape = ((GeometryAtom)shapeList.get(i)).source; shape.removeLight(mLgts[k]); } mLgts[k].isScoped = false; } } /** * Clones only the retained side, internal use only */ @Override protected Object clone() { LightRetained lr = (LightRetained)super.clone(); lr.color = new Color3f(color); lr.scopes = new Vector(scopes); lr.initInfluencingBoundingLeaf(getInfluencingBoundingLeaf()); lr.region = null; lr.lightDirty = 0xffff; lr.sgLightDirty = 0xffff; lr.universe = null; lr.isNeeded = false; lr.inImmCtx = false; lr.sgLight = null; lr.key = null; lr.mirrorLights = new LightRetained[1]; lr.numMirrorLights = 0; lr.environmentSets = new UnorderList(1, EnvironmentSet.class); return lr; } // Called during RenderingEnv object update @Override void updateTransformChange() { } // Called on mirror object and updated when message is received void updateImmediateTransformChange() { // If bounding leaf is null, tranform the bounds object if (boundingLeaf == null) { if (regionOfInfluence != null) { region = regionOfInfluence.copy(region); region.transform(regionOfInfluence, getCurrentLocalToVworld()); } } } void sendMessage(int attrMask, Object attr) { J3dMessage createMessage = new J3dMessage(); createMessage.threads = targetThreads; createMessage.type = J3dMessage.LIGHT_CHANGED; createMessage.universe = universe; createMessage.args[0] = this; createMessage.args[1]= new Integer(attrMask); if (inSharedGroup) createMessage.args[2] = new Integer(numMirrorLights); else createMessage.args[2] = new Integer(1); createMessage.args[3] = mirrorLights.clone(); createMessage.args[4] = attr; VirtualUniverse.mc.processMessage(createMessage); } @Override void mergeTransform(TransformGroupRetained xform) { super.mergeTransform(xform); if (regionOfInfluence != null) { regionOfInfluence.transform(xform.transform); } } }