/* * Copyright 1999-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.Collection; import java.util.HashMap; import java.util.Iterator; /** * The TextureBin manages a collection of TextureSetting objects. * All objects in the TextureBin share the same Texture reference. */ //class TextureBin extends Object implements ObjectUpdate, NodeComponentUpdate { class TextureBin extends Object implements ObjectUpdate { TextureUnitStateRetained [] texUnitState = null; // number of active texture unit private int numActiveTexUnit; /** * The RenderBin for this object */ RenderBin renderBin = null; /** * The EnvironmentSet that this TextureBin resides */ EnvironmentSet environmentSet = null; /** * The AttributeBin that this TextureBin resides */ AttributeBin attributeBin = null; /** * The ShaderBin that this TextureBin resides */ ShaderBin shaderBin = null; /** * The references to the next and previous TextureBins in the * list. */ TextureBin next = null; TextureBin prev = null; /** * Oring of the equivalence bits for all appearance attrs under * this renderBin */ int equivalent = 0; /** * If any of the texture reference in an appearance is frequently * changable, then a separate TextureBin will be created for this * appearance, and this TextureBin is marked as the sole user of * this appearance, and app will be pointing to the appearance * being referenced by this TextureBin. Otherwise, app is null */ AppearanceRetained app = null; /** * Sole user node component dirty mask. The first bit is reserved * for node component reference dirty bit. It is set if any of the * texture related node component reference in the appearance is * being modified. The second bit onwords are for the individual * TextureUnitState dirty bit. The ith bit set means the (i-1) * texture unit state is modified. Note, this mask only supports * 30 texture unit states. If the appearance uses more than 31 * texture unit states, then the modification of the 32nd texture * unit state and up will have the first bit set, that means * the TextureBin will be reset, rather than only the particular * texture unit state will be reset. */ int soleUserCompDirty; static final int SOLE_USER_DIRTY_REF = 0x1; static final int SOLE_USER_DIRTY_TA = 0x2; static final int SOLE_USER_DIRTY_TC = 0x4; static final int SOLE_USER_DIRTY_TEXTURE = 0x8; static final int SOLE_USER_DIRTY_TUS = 0x10; /** * The hashMap of RenderMolecules in this TextureBin this is used in rendering, * the key used is localToVworld */ HashMap> addOpaqueRMs = new HashMap>(); HashMap> addTransparentRMs = new HashMap>(); // A hashmap based on localToVworld for fast // insertion of new renderMolecules HashMap opaqueRenderMoleculeMap = new HashMap(); HashMap transparentRenderMoleculeMap = new HashMap(); // List of renderMolecules - used in rendering .. RenderMolecule opaqueRMList = null; RenderMolecule transparentRMList = null; TransparentRenderingInfo parentTInfo; int numRenderMolecules = 0; int numEditingRenderMolecules = 0; int tbFlag = 0; // a general bitmask for TextureBin // Following are the bits used in flag final static int ON_RENDER_BIN_LIST = 0x0001; final static int ON_UPDATE_LIST = 0x0002; final static int SOLE_USER = 0x0004; final static int CONTIGUOUS_ACTIVE_UNITS = 0x0008; final static int RESORT = 0x0010; final static int ON_UPDATE_CHECK_LIST = 0x0020; final static int USE_DISPLAYLIST = -2; final static int USE_VERTEXARRAY = -1; TextureBin(TextureUnitStateRetained[] state, AppearanceRetained app, RenderBin rb) { renderBin = rb; tbFlag = 0; reset(state, app); } /** * For now, clone everything just like the other NodeComponent */ void reset(TextureUnitStateRetained[] state, AppearanceRetained app) { prev = null; next = null; opaqueRMList = null; transparentRMList = null; numEditingRenderMolecules = 0; // Issue 249 - check for sole user only if property is set // determine if this appearance is a sole user of this // TextureBin tbFlag &= ~TextureBin.SOLE_USER; if (VirtualUniverse.mc.allowSoleUser) { if ((app != null) && (app.changedFrequent & (AppearanceRetained.TEXTURE | AppearanceRetained.TEXCOORD_GEN | AppearanceRetained.TEXTURE_ATTR | AppearanceRetained.TEXTURE_UNIT_STATE)) != 0) { tbFlag |= TextureBin.SOLE_USER; } } if ((tbFlag & TextureBin.SOLE_USER) != 0) { this.app = app; } else { this.app = null; } resetTextureState(state); if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) == 0) { renderBin.addTextureBin(this); tbFlag |= TextureBin.ON_RENDER_BIN_LIST; } } void resetTextureState(TextureUnitStateRetained[] state) { int i; boolean foundDisableUnit = false; numActiveTexUnit = 0; boolean soleUser = ((tbFlag & TextureBin.SOLE_USER) != 0); TextureRetained prevFirstTexture = null; TextureRetained tex; tbFlag |= TextureBin.CONTIGUOUS_ACTIVE_UNITS; if (state != null) { foundDisableUnit = false; if (texUnitState == null || (texUnitState.length != state.length)) { texUnitState = new TextureUnitStateRetained[state.length]; } else if (texUnitState.length > 0 && texUnitState[0] != null) { prevFirstTexture = texUnitState[0].texture; } for (i = 0; i < state.length; i++) { if (state[i] == null) { texUnitState[i] = null; foundDisableUnit = true; } else { // create a clone texture unit state if (texUnitState[i] == null) { texUnitState[i] = new TextureUnitStateRetained(); } // for sole user TextureUnitState, save // the node component reference in the mirror reference // of the cloned copy for equal test, and // for native download optimization if (soleUser || state[i].changedFrequent != 0) { texUnitState[i].mirror = state[i]; } // for the lowest level of node component in // TextureBin, clone it only if it is not // changedFrequent; in other words, if the // lowest level of texture related node components // such as TextureAttributes & TexCoordGen is // changedFrequent, have the cloned texUnitState // reference the mirror of those node components // directly. For Texture, we'll always reference // the mirror. // decrement the TextureBin ref count of the previous // texture tex = texUnitState[i].texture; if (tex != null) { tex.decTextureBinRefCount(this); if (soleUser && (tex.getTextureBinRefCount(this) == 0) && (tex != state[i].texture)) { // In this case texture change but // TextureBin will not invoke clear() to reset. // So we need to free the texture resource here. renderBin.addTextureResourceFreeList(tex); } } texUnitState[i].texture = state[i].texture; // increment the TextureBin ref count of the new // texture if (texUnitState[i].texture != null) { texUnitState[i].texture.incTextureBinRefCount(this); } if (state[i].texAttrs != null) { if (state[i].texAttrs.changedFrequent != 0) { texUnitState[i].texAttrs = state[i].texAttrs; } else { // need to check for texAttrs.source because // texAttrs could be pointing to the mirror // in the last frame, so don't want to // overwrite the mirror if (texUnitState[i].texAttrs == null || texUnitState[i].texAttrs.source != null) { texUnitState[i].texAttrs = new TextureAttributesRetained(); } texUnitState[i].texAttrs.set( state[i].texAttrs); texUnitState[i].texAttrs.mirrorCompDirty = true; // for sole user TextureBin, we are saving // the mirror node component in the mirror // reference in the clone object. This // will be used in state download to // avoid redundant download if (soleUser) { texUnitState[i].texAttrs.mirror = state[i].texAttrs; } else { texUnitState[i].texAttrs.mirror = null; } } } else { texUnitState[i].texAttrs = null; } if (state[i].texGen != null) { if (state[i].texGen.changedFrequent != 0) { texUnitState[i].texGen = state[i].texGen; } else { // need to check for texGen.source because // texGen could be pointing to the mirror // in the last frame, so don't want to // overwrite the mirror if (texUnitState[i].texGen == null || texUnitState[i].texGen.source != null) { texUnitState[i].texGen = new TexCoordGenerationRetained(); } texUnitState[i].texGen.set(state[i].texGen); texUnitState[i].texGen.mirrorCompDirty = true; // for sole user TextureBin, we are saving // the mirror node component in the mirror // reference in the clone object. This // will be used in state download to // avoid redundant download if (soleUser) { texUnitState[i].texGen.mirror = state[i].texGen; } else { texUnitState[i].texGen.mirror = null; } } } else { texUnitState[i].texGen = null; } // Track the last active texture unit and the total number // of active texture units. Note that this total number // now includes disabled units so that there is always // a one-to-one mapping. We no longer remap texture units. if (texUnitState[i].isTextureEnabled()) { numActiveTexUnit = i + 1; if (foundDisableUnit) { // mark that active texture units are not // contiguous tbFlag &= ~TextureBin.CONTIGUOUS_ACTIVE_UNITS; } } else { foundDisableUnit = true; } } } // check to see if the TextureBin sorting criteria is // modified for this textureBin; if yes, mark that // resorting is needed if ((texUnitState[0] == null && prevFirstTexture != null) || (texUnitState[0] != null && texUnitState[0].texture != prevFirstTexture)) { tbFlag |= TextureBin.RESORT; } } else { // check to see if the TextureBin sorting criteria is // modified for this textureBin; if yes, mark that // resorting is needed if (texUnitState != null && texUnitState[0].texture != null) { tbFlag |= TextureBin.RESORT; } texUnitState = null; } soleUserCompDirty = 0; } /** * The TextureBin is to be removed from RenderBin, * do the proper unsetting of any references */ void clear() { // make sure there is no reference to the scenegraph app = null; // for each texture referenced in the texture units, decrement // the reference count. If the reference count == 0, tell // the renderer to free up the resource if (texUnitState != null) { TextureRetained tex; for (int i = 0; i < texUnitState.length; i++) { if (texUnitState[i] != null) { if (texUnitState[i].texture != null) { tex = texUnitState[i].texture; tex.decTextureBinRefCount(this); if (tex.getTextureBinRefCount(this) == 0) { renderBin.addTextureResourceFreeList(tex); } texUnitState[i].texture = null; } // make sure there is no more reference to the scenegraph texUnitState[i].mirror = null; texUnitState[i].texture = null; if (texUnitState[i].texAttrs != null && texUnitState[i].texAttrs.source != null) { texUnitState[i].texAttrs = null; } if (texUnitState[i].texGen != null && texUnitState[i].texGen.source != null) { texUnitState[i].texGen = null; } } } } } /** * This tests if the qiven textureUnitState matches this TextureBin */ boolean equals(TextureUnitStateRetained state[], RenderAtom ra) { // if this TextureBin is a soleUser case or the incoming // app has changedFrequent bit set for any of the texture // related component, then either the current TextureBin // or the incoming app requires the same app match if (((tbFlag & TextureBin.SOLE_USER) != 0) || ((ra.app != null) && (ra.app.changedFrequent & (AppearanceRetained.TEXTURE | AppearanceRetained.TEXCOORD_GEN | AppearanceRetained.TEXTURE_ATTR | AppearanceRetained.TEXTURE_UNIT_STATE)) != 0)) { if (app == ra.app) { // if this textureBin is currently on a zombie state, // we'll need to put it on the update list to reevaluate // the state, because while it is on a zombie state, // texture state could have been changed. Example, // application could have detached an appearance, // made changes to the texture references, and then // reattached the appearance. In this case, the texture // changes would not have reflected to the textureBin if (numEditingRenderMolecules == 0) { //System.err.println("===> TB in zombie state " + this); if (soleUserCompDirty == 0) { this.renderBin.tbUpdateList.add(this); } soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_REF; } return true; } else { return false; } } if (texUnitState == null && state == null) return (true); if (texUnitState == null || state == null) return (false); if (state.length != texUnitState.length) return (false); for (int i = 0; i < texUnitState.length; i++) { // If texture Unit State is null if (texUnitState[i] == null) { if (state[i] != null) return (false); } else { if (!texUnitState[i].equivalent(state[i])) { return (false); } } } // Check if the image component has changed(may be a clearLive texture // change img component. setLive case) // if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) == 0) { renderBin.addTextureBin(this); tbFlag |= TextureBin.ON_RENDER_BIN_LIST; } return (true); } /* // updateNodeComponentCheck is called for each soleUser TextureBin // into which new renderAtom has been added. This method is called before // updateNodeComponent() to allow TextureBin to catch any node // component changes that have been missed because the changes // come when there is no active renderAtom associated with the // TextureBin. See bug# 4503926 for details. public void updateNodeComponentCheck() { //System.err.println("TextureBin.updateNodeComponentCheck()"); tbFlag &= ~TextureBin.ON_UPDATE_CHECK_LIST; if ((soleUserCompDirty & SOLE_USER_DIRTY_REF) != 0) { return ; } if ((app.compChanged & (AppearanceRetained.TEXTURE | AppearanceRetained.TEXCOORD_GEN | AppearanceRetained.TEXTURE_ATTR | AppearanceRetained.TEXTURE_UNIT_STATE)) != 0) { if (soleUserCompDirty == 0) { this.renderBin.tbUpdateList.add(this); } soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_REF; } else if (app.texUnitState != null) { // if one texture unit state has to be reevaluated, then // it's enough update checking because reevaluating texture unit // state will automatically take care of its node component // updates. boolean done = false; for (int i = 0; i < app.texUnitState.length && !done; i++) { if (app.texUnitState[i] != null) { if (app.texUnitState[i].compChanged != 0) { if (soleUserCompDirty == 0) { this.renderBin.tbUpdateList.add(this); } soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TUS; done = true; } else { if (app.texUnitState[i].texAttrs != null && app.texUnitState[i].texAttrs.compChanged != 0) { if (soleUserCompDirty == 0) { this.renderBin.tbUpdateList.add(this); } soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TA; } if (app.texUnitState[i].texGen != null && app.texUnitState[i].texGen.compChanged != 0) { if (soleUserCompDirty == 0) { this.renderBin.tbUpdateList.add(this); } soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TC; } if (app.texUnitState[i].texture != null && ((app.texUnitState[i].texture.compChanged & TextureRetained.ENABLE_CHANGED) != 0)) { if (soleUserCompDirty == 0) { this.renderBin.tbUpdateList.add(this); } soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TEXTURE; } } } } } } */ /** * updateNodeComponent is called from RenderBin to update the * clone copy of the sole user node component in TextureBin when the * corresponding node component is being modified */ public void updateNodeComponent() { // don't bother to update if the TextureBin is already // removed from RenderBin if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) == 0) return; // if any of the texture reference in the appearance referenced // by a sole user TextureBin is being modified, just do a reset if (((tbFlag & TextureBin.SOLE_USER) != 0) && ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_REF) != 0)) { resetTextureState(app.texUnitState); return; } if (texUnitState == null) { soleUserCompDirty = 0; return; } if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TUS) != 0) { // Now take care of the Texture Unit State changes TextureUnitStateRetained tus, mirrorTUS = null; boolean soleUser = ((tbFlag & TextureBin.SOLE_USER) != 0); for (int i = 0; i < texUnitState.length; i++) { tus = texUnitState[i]; if (tus != null) { if (tus.mirror != null) { mirrorTUS = (TextureUnitStateRetained)tus.mirror; if (tus.texture != mirrorTUS.texture) { if (tus.texture != null) { tus.texture.decTextureBinRefCount(this); } tus.texture = mirrorTUS.texture; if (tus.texture != null) { tus.texture.incTextureBinRefCount(this); } // the first texture (TextureBin sorting // criteria) is modified, so needs to resort if (i == 0) { tbFlag |= TextureBin.RESORT; } } if (mirrorTUS.texAttrs != null) { if (mirrorTUS.texAttrs.changedFrequent != 0) { tus.texAttrs = mirrorTUS.texAttrs; } else { if (tus.texAttrs == null || tus.texAttrs.source != null) { tus.texAttrs = new TextureAttributesRetained(); } tus.texAttrs.set(mirrorTUS.texAttrs); tus.texAttrs.mirrorCompDirty = true; if (soleUser) { tus.texAttrs.mirror = mirrorTUS.texAttrs; } else { tus.texAttrs.mirror = null; } } } else { tus.texAttrs = null; } if (mirrorTUS.texGen != null) { if (mirrorTUS.texGen.changedFrequent != 0) { tus.texGen = mirrorTUS.texGen; } else { if (tus.texGen == null || tus.texGen.source != null) { tus.texGen = new TexCoordGenerationRetained(); } tus.texGen.set(mirrorTUS.texGen); tus.texGen.mirrorCompDirty = true; if (soleUser) { tus.texGen.mirror = mirrorTUS.texGen; } else { tus.texGen.mirror = null; } } } else { tus.texGen = null; } } } } // need to reEvaluate # of active textures after the update soleUserCompDirty |= TextureBin.SOLE_USER_DIRTY_TEXTURE; // TextureUnitState update automatically taken care of // TextureAttributes & TexCoordGeneration update soleUserCompDirty &= ~(TextureBin.SOLE_USER_DIRTY_TA | TextureBin.SOLE_USER_DIRTY_TC); } if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TEXTURE) != 0) { boolean foundDisableUnit = false; numActiveTexUnit = 0; tbFlag |= TextureBin.CONTIGUOUS_ACTIVE_UNITS; for (int i = 0; i < texUnitState.length; i++) { // Track the last active texture unit and the total number // of active texture units. Note that this total number // now includes disabled units so that there is always // a one-to-one mapping. We no longer remap texture units. if (texUnitState[i] != null && texUnitState[i].isTextureEnabled()) { numActiveTexUnit = i + 1; if (foundDisableUnit) { // mark that active texture units are not // contiguous tbFlag &= ~TextureBin.CONTIGUOUS_ACTIVE_UNITS; } } else { foundDisableUnit = true; } } } if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TA) != 0) { for (int i = 0; i < texUnitState.length; i++) { if (texUnitState[i] != null && texUnitState[i].texAttrs != null && texUnitState[i].texAttrs.mirror != null && texUnitState[i].texAttrs.mirror.changedFrequent != 0) { texUnitState[i].texAttrs = (TextureAttributesRetained) texUnitState[i].texAttrs.mirror; } } } if ((soleUserCompDirty & TextureBin.SOLE_USER_DIRTY_TC) != 0) { for (int i = 0; i < texUnitState.length; i++) { if (texUnitState[i] != null && texUnitState[i].texGen != null && texUnitState[i].texGen.mirror != null && texUnitState[i].texGen.mirror.changedFrequent != 0) { texUnitState[i].texGen = (TexCoordGenerationRetained) texUnitState[i].texGen.mirror; } } } soleUserCompDirty = 0; } @Override public void updateObject() { if (!addOpaqueRMs.isEmpty()) { opaqueRMList = addAll(opaqueRenderMoleculeMap, addOpaqueRMs, opaqueRMList, true); } if (!addTransparentRMs.isEmpty()) { // If transparent and not in bg geometry and inodepth // sorted transparency if (transparentRMList == null && (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE || environmentSet.lightBin.geometryBackground != null)) { // System.err.println("========> addTransparentTextureBin "+this); transparentRMList = addAll(transparentRenderMoleculeMap, addTransparentRMs, transparentRMList, false); // Eventhough we are adding to transparentList , if all the RMS // have been switched already due to changeLists, then there is // nothing to add, and TBIN does not have any transparentRMList if (transparentRMList != null) { renderBin.addTransparentObject(this); } } else { transparentRMList = addAll(transparentRenderMoleculeMap, addTransparentRMs, transparentRMList, false); } } tbFlag &= ~TextureBin.ON_UPDATE_LIST; } /** * Each list of renderMoledule with the same localToVworld * is connect by rm.next and rm.prev. * At the end of the list (i.e. rm.next = null) the field * rm.nextMap is link to another list (with the same * localToVworld). So during rendering it will traverse * rm.next until this is null, then follow the .nextMap * to access another list and use rm.next to continue * until both rm.next and rm.nextMap are null. * * renderMoleculeMap is use to assist faster location of * renderMolecule List with the same localToVWorld. The * start of renderMolecule in the list with same * localToVworld is insert in renderMoleculeMap. This * map is clean up at removeRenderMolecule(). TextureBin * also use the map for quick location of renderMolecule * with the same localToVworld and attributes in * findRenderMolecule(). */ RenderMolecule addAll(HashMap renderMoleculeMap, HashMap> addRMs, RenderMolecule startList, boolean opaqueList) { int i; Collection> c = addRMs.values(); Iterator> listIterator = c.iterator(); RenderMolecule renderMoleculeList, head; while (listIterator.hasNext()) { boolean changed = false; ArrayList curList = listIterator.next(); RenderMolecule r = curList.get(0); // If this is a opaque one , but has been switched to a transparentList or // vice-versa (dur to changeLists function called before this), then // do nothing! // For changedFrequent case: Consider the case when a RM is added // (so is in the addRM list) and then // a change in transparent value occurs that make it from opaque to // transparent (the switch is handled before this function is called) if (r.isOpaqueOrInOG != opaqueList) { continue; } // Get the list of renderMolecules for this transform renderMoleculeList = renderMoleculeMap.get(r.localToVworld); if (renderMoleculeList == null) { renderMoleculeList = r; renderMoleculeMap.put(r.localToVworld, renderMoleculeList); // Add this renderMolecule at the beginning of RM list if (startList == null) { startList = r; r.nextMap = null; r.prevMap = null; startList.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS; } else { r.nextMap = startList; startList.prevMap = r; startList = r; startList.nextMap.checkEquivalenceWithLeftNeighbor(r, RenderMolecule.ALL_DIRTY_BITS); } } else { // Insert the renderMolecule next to a RM that has equivalent // texture unit state if ((head = insertRenderMolecule(r, renderMoleculeList)) != null) { if (renderMoleculeList.prevMap != null) { renderMoleculeList.prevMap.nextMap = head; } head.prevMap = renderMoleculeList.prevMap; renderMoleculeList.prevMap = null; renderMoleculeList = head; changed = true; } } for (i = 1; i < curList.size(); i++) { r = curList.get(i); // If this is a opaque one , but has been switched to a transparentList or // vice-versa (dur to changeLists function called before this), then // do nothing! // For changedFrequent case: Consider the case when a RM is added // (so is in the addRM list) and then // a change in transparent value occurs that make it from opaque to // transparent (the switch is handled before this function is called) if (r.isOpaqueOrInOG != opaqueList) continue; if ((head = insertRenderMolecule(r, renderMoleculeList)) != null) { if (renderMoleculeList.prevMap != null) { renderMoleculeList.prevMap.nextMap = head; } head.prevMap = renderMoleculeList.prevMap; renderMoleculeList.prevMap = null; renderMoleculeList = head; changed = true; } } if (changed) { renderMoleculeMap.put(r.localToVworld, renderMoleculeList); if (renderMoleculeList.prevMap != null) { renderMoleculeList.checkEquivalenceWithLeftNeighbor( renderMoleculeList.prevMap, RenderMolecule.ALL_DIRTY_BITS); } else { startList = renderMoleculeList; startList.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS; } } } addRMs.clear(); return startList; } // XXXX: Could the analysis be done during insertRenderMolecule? // Return the head of the list, // if the insertion occurred at beginning of the list RenderMolecule insertRenderMolecule(RenderMolecule r, RenderMolecule renderMoleculeList) { RenderMolecule rm, retval; // Look for a RM that has an equivalent material rm = renderMoleculeList; while (rm != null) { if (rm.material == r.material || (rm.definingMaterial != null && rm.definingMaterial.equivalent(r.definingMaterial))) { // Put it here r.next = rm; r.prev = rm.prev; if (rm.prev == null) { renderMoleculeList = r; retval = renderMoleculeList; } else { rm.prev.next = r; retval = null; } rm.prev = r; r.checkEquivalenceWithBothNeighbors(RenderMolecule.ALL_DIRTY_BITS); return retval; } // If they are not equivalent, then skip to the first one // that has a different material using the dirty bits else { rm = rm.next; while (rm != null && ((rm.dirtyAttrsAcrossRms & RenderMolecule.MATERIAL_DIRTY) == 0)) { rm = rm.next; } } } // Just put it up front r.next = renderMoleculeList; renderMoleculeList.prev = r; renderMoleculeList = r; r.checkEquivalenceWithBothNeighbors(RenderMolecule.ALL_DIRTY_BITS); return renderMoleculeList; } /** * Adds the given RenderMolecule to this TextureBin */ void addRenderMolecule(RenderMolecule r, RenderBin rb) { r.textureBin = this; HashMap> map; if (r.isOpaqueOrInOG) map = addOpaqueRMs; else map = addTransparentRMs; ArrayList list = map.get(r.localToVworld); if (list == null) { list = new ArrayList(); map.put(r.localToVworld, list); } list.add(r); if ((tbFlag & TextureBin.ON_UPDATE_LIST) == 0) { tbFlag |= TextureBin.ON_UPDATE_LIST; rb.objUpdateList.add(this); } } /** * Removes the given RenderMolecule from this TextureBin */ void removeRenderMolecule(RenderMolecule r) { int index; boolean found = false; RenderMolecule rmlist; HashMap> addMap; HashMap allMap; r.textureBin = null; if (r.isOpaqueOrInOG) { rmlist = opaqueRMList; allMap = opaqueRenderMoleculeMap; addMap = addOpaqueRMs; } else { rmlist = transparentRMList; allMap = transparentRenderMoleculeMap; addMap = addTransparentRMs; } // If the renderMolecule being remove is contained in addRMs, then // remove the renderMolecule from the addList ArrayList list = addMap.get(r.localToVworld); if (list != null) { if ((index = list.indexOf(r)) != -1) { list.remove(index); // If this was the last element for this localToVworld, then remove // the entry from the addRMs list if (list.isEmpty()) { addMap.remove(r.localToVworld); } r.prev = null; r.next = null; found = true; } } if (!found) { RenderMolecule head = removeOneRM(r, allMap, rmlist); r.soleUserCompDirty = 0; r.onUpdateList = 0; if (r.definingPolygonAttributes != null && (r.definingPolygonAttributes.changedFrequent != 0)) r.definingPolygonAttributes = null; if (r.definingLineAttributes != null && (r.definingLineAttributes.changedFrequent != 0)) r.definingLineAttributes = null; if (r.definingPointAttributes != null && (r.definingPointAttributes.changedFrequent != 0)) r.definingPointAttributes = null; if (r.definingMaterial != null && (r.definingMaterial.changedFrequent != 0)) r.definingMaterial = null; if (r.definingColoringAttributes != null && (r.definingColoringAttributes.changedFrequent != 0)) r.definingColoringAttributes = null; if (r.definingTransparency != null && (r.definingTransparency.changedFrequent != 0)) r.definingTransparency = null; renderBin.removeRenderMolecule(r); if (r.isOpaqueOrInOG) { opaqueRMList = head; } else { transparentRMList = head; } } // If the renderMolecule removed is not opaque then .. if (!r.isOpaqueOrInOG && transparentRMList == null && (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE || environmentSet.lightBin.geometryBackground != null)) { renderBin.removeTransparentObject(this); } // If the rm removed is the one that is referenced in the tinfo // then change this reference else if (parentTInfo != null && parentTInfo.rm == r) { parentTInfo.rm = transparentRMList; } // Removal of this texture setting from the texCoordGenartion // is done during the removeRenderAtom routine in RenderMolecule.java // Only remove this texture bin if there are no more renderMolcules // waiting to be added if (opaqueRenderMoleculeMap.isEmpty() && addOpaqueRMs.isEmpty() && transparentRenderMoleculeMap.isEmpty() && addTransparentRMs.isEmpty()) { if ((tbFlag & TextureBin.ON_RENDER_BIN_LIST) != 0) { tbFlag &= ~TextureBin.ON_RENDER_BIN_LIST; renderBin.removeTextureBin(this); } shaderBin.removeTextureBin(this); texUnitState = null; } } /** * This method is called to update the state for this * TextureBin. This is only applicable in the single-pass case. * Multi-pass render will have to take care of its own * state update. */ void updateAttributes(Canvas3D cv) { boolean dirty = ((cv.canvasDirty & (Canvas3D.TEXTUREBIN_DIRTY| Canvas3D.TEXTUREATTRIBUTES_DIRTY)) != 0); if (cv.textureBin == this && !dirty) { return; } cv.textureBin = this; // save the current number of active texture unit so as // to be able to reset the one that is not enabled in this bin int lastActiveTexUnitIdx = -1; // Get the number of available texture units; this depends on // whether or not shaders are being used. boolean useShaders = (shaderBin.shaderProgram != null); int availableTextureUnits = useShaders ? cv.maxTextureImageUnits : cv.maxTextureUnits; // If the number of active texture units is greater than the number of // supported units, then we // need to set a flag indicating that the texture units are invalid. boolean disableTexture = false; if (numActiveTexUnit > availableTextureUnits) { disableTexture = true; // System.err.println("*** TextureBin : number of texture units exceeded"); } // set the number active texture unit in Canvas3D if (disableTexture) { cv.setNumActiveTexUnit(0); } else { cv.setNumActiveTexUnit(numActiveTexUnit); } // state update if (numActiveTexUnit <= 0 || disableTexture) { if (cv.getLastActiveTexUnit() >= 0) { // no texture units enabled // when the canvas supports multi texture units, // we'll need to reset texture for all texture units if (cv.multiTexAccelerated) { for (int i = 0; i <= cv.getLastActiveTexUnit(); i++) { cv.resetTexture(cv.ctx, i); } // set the active texture unit back to 0 cv.setNumActiveTexUnit(0); cv.activeTextureUnit(cv.ctx, 0); } else { cv.resetTexture(cv.ctx, -1); } cv.setLastActiveTexUnit(-1); } } else { int j = 0; for (int i = 0; i < texUnitState.length; i++) { if (j >= cv.texUnitState.length) { // We finish enabling the texture state. // Note that it is possible // texUnitState.length > cv.texUnitState.length break; } if ((texUnitState[i] != null) && texUnitState[i].isTextureEnabled()) { if (dirty || cv.texUnitState[j].mirror == null || cv.texUnitState[j].mirror != texUnitState[i].mirror) { // update the texture unit state texUnitState[i].updateNative(j, cv, false, false); cv.texUnitState[j].mirror = texUnitState[i].mirror; } // create a mapping that maps an active texture // unit to a texture unit state lastActiveTexUnitIdx = j; } else { if (j <= cv.getLastActiveTexUnit()) { cv.resetTexture(cv.ctx, j); } } j++; } // make sure to disable the remaining texture units // since they could have been enabled from the previous // texture bin for (int i = j; i <= cv.getLastActiveTexUnit(); i++) { cv.resetTexture(cv.ctx, i); } cv.setLastActiveTexUnit(lastActiveTexUnitIdx); // set the active texture unit back to 0 cv.activeTextureUnit(cv.ctx, 0); } cv.canvasDirty &= ~Canvas3D.TEXTUREBIN_DIRTY; } /** * Renders this TextureBin */ void render(Canvas3D cv) { render(cv, opaqueRMList); } void render(Canvas3D cv, RenderMolecule rlist) { // include this TextureBin to the to-be-updated state set in canvas cv.setStateToUpdate(Canvas3D.TEXTUREBIN_BIT, this); renderList(cv, USE_DISPLAYLIST, rlist); } void render(Canvas3D cv, TransparentRenderingInfo rlist) { // include this TextureBin to the to-be-updated state set in canvas cv.setStateToUpdate(Canvas3D.TEXTUREBIN_BIT, this); renderList(cv, USE_DISPLAYLIST, rlist); } /** * render list of RenderMolecule */ void renderList(Canvas3D cv, int pass, RenderMolecule rlist) { assert pass < 0; // bit mask of all attr fields that are equivalent across // renderMolecules thro. ORing of invisible RMs. int combinedDirtyBits = 0; boolean rmVisible = true; RenderMolecule rm = rlist; while (rm != null) { if(rmVisible) { combinedDirtyBits = rm.dirtyAttrsAcrossRms; } else { combinedDirtyBits |= rm.dirtyAttrsAcrossRms; } rmVisible = rm.render(cv, pass, combinedDirtyBits); // next render molecule or the nextmap if (rm.next == null) { rm = rm.nextMap; } else { rm = rm.next; } } } /** * render sorted transparent list */ void renderList(Canvas3D cv, int pass, TransparentRenderingInfo tinfo) { assert pass < 0; RenderMolecule rm = tinfo.rm; if (rm.isSwitchOn()) { rm.transparentSortRender(cv, pass, tinfo); } } void changeLists(RenderMolecule r) { RenderMolecule renderMoleculeList, rmlist = null, head; HashMap allMap = null; boolean newRM = false; // System.err.println("changeLists r = "+r+" tBin = "+this); // If its a new RM then do nothing, otherwise move lists if (r.isOpaqueOrInOG) { if (opaqueRMList == null && (r.prev == null && r.prevMap == null && r.next == null && r.nextMap == null)) { newRM = true; } else { rmlist = opaqueRMList; allMap = opaqueRenderMoleculeMap; } } else { if (transparentRMList == null && (r.prev == null && r.prevMap == null && r.next == null && r.nextMap == null) ){ newRM = true; } else { rmlist = transparentRMList; allMap = transparentRenderMoleculeMap; } } if (!newRM) { head = removeOneRM(r, allMap, rmlist); if (r.isOpaqueOrInOG) { opaqueRMList = head; } else { transparentRMList = head; if (transparentRMList == null && (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE || environmentSet.lightBin.geometryBackground != null)) { renderBin.removeTransparentObject(this); } // Issue 129: remove the RM's render atoms from the // list of transparent render atoms if ((renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) && (environmentSet.lightBin.geometryBackground == null)) { r.addRemoveTransparentObject(renderBin, false); } } } HashMap renderMoleculeMap; RenderMolecule startList; // Now insert in the other bin r.evalAlphaUsage(shaderBin.attributeBin.definingRenderingAttributes, texUnitState); r.isOpaqueOrInOG = r.isOpaque() ||r.inOrderedGroup; if (r.isOpaqueOrInOG) { startList = opaqueRMList; renderMoleculeMap = opaqueRenderMoleculeMap; markDlistAsDirty(r); } else { startList = transparentRMList; renderMoleculeMap = transparentRenderMoleculeMap; if ((r.primaryMoleculeType &RenderMolecule.DLIST_MOLECULE) != 0 && renderBin.transpSortMode != View.TRANSPARENCY_SORT_NONE) { renderBin.addDisplayListResourceFreeList(r); renderBin.removeDirtyRenderMolecule(r); r.vwcBounds.set(null); r.displayListId = 0; r.displayListIdObj = null; // Change the group type for all the rlistInfo in the primaryList RenderAtomListInfo rinfo = r.primaryRenderAtomList; while (rinfo != null) { rinfo.groupType = RenderAtom.SEPARATE_DLIST_PER_RINFO; if (rinfo.renderAtom.dlistIds == null) { rinfo.renderAtom.dlistIds = new int[rinfo.renderAtom.rListInfo.length]; for (int k = 0; k < rinfo.renderAtom.dlistIds.length; k++) { rinfo.renderAtom.dlistIds[k] = -1; } } if (rinfo.renderAtom.dlistIds[rinfo.index] == -1) { rinfo.renderAtom.dlistIds[rinfo.index] = VirtualUniverse.mc.getDisplayListId().intValue(); renderBin.addDlistPerRinfo.add(rinfo); } rinfo = rinfo.next; } r.primaryMoleculeType = RenderMolecule.SEPARATE_DLIST_PER_RINFO_MOLECULE; } else { markDlistAsDirty(r); } } renderMoleculeList = renderMoleculeMap.get(r.localToVworld); if (renderMoleculeList == null) { renderMoleculeList = r; renderMoleculeMap.put(r.localToVworld, renderMoleculeList); // Add this renderMolecule at the beginning of RM list if (startList == null) { startList = r; r.nextMap = null; r.prevMap = null; } else { r.nextMap = startList; startList.prevMap = r; startList = r; startList.nextMap.checkEquivalenceWithLeftNeighbor(r,RenderMolecule.ALL_DIRTY_BITS); } // Issue 67 : since we are adding the new RM at the head, we must // set all dirty bits unconditionally startList.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS; } else { // Insert the renderMolecule next to a RM that has equivalent // texture unit state if ((head = insertRenderMolecule(r, renderMoleculeList)) != null) { if (renderMoleculeList.prevMap != null) { renderMoleculeList.prevMap.nextMap = head; } head.prevMap = renderMoleculeList.prevMap; renderMoleculeList.prevMap = null; renderMoleculeList = head; renderMoleculeMap.put(r.localToVworld, renderMoleculeList); if (renderMoleculeList.prevMap != null) { renderMoleculeList.checkEquivalenceWithLeftNeighbor(renderMoleculeList.prevMap, RenderMolecule.ALL_DIRTY_BITS); } else { startList.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS; startList = renderMoleculeList; } } } if (r.isOpaqueOrInOG) { opaqueRMList = startList; } else { // If transparent and not in bg geometry and inodepth sorted transparency if (transparentRMList == null&& (renderBin.transpSortMode == View.TRANSPARENCY_SORT_NONE || environmentSet.lightBin.geometryBackground != null)) { transparentRMList = startList; renderBin.addTransparentObject(this); } else { transparentRMList = startList; } // Issue 129: add the RM's render atoms to the list of // transparent render atoms // XXXX: do we need to resort the list after the add??? if ((renderBin.transpSortMode == View.TRANSPARENCY_SORT_GEOMETRY) && (environmentSet.lightBin.geometryBackground == null)) { r.addRemoveTransparentObject(renderBin, true); } } } RenderMolecule removeOneRM(RenderMolecule r, HashMap allMap, RenderMolecule list) { RenderMolecule rmlist = list; // In the middle, just remove and update if (r.prev != null && r.next != null) { r.prev.next = r.next; r.next.prev = r.prev; r.next.checkEquivalenceWithLeftNeighbor(r.prev,RenderMolecule.ALL_DIRTY_BITS); } // If whats is removed is at the end of an entry else if (r.prev != null && r.next == null) { r.prev.next = r.next; r.prev.nextMap = r.nextMap; if (r.nextMap != null) { r.nextMap.prevMap = r.prev; r.nextMap.checkEquivalenceWithLeftNeighbor(r.prev,RenderMolecule.ALL_DIRTY_BITS); } } else if (r.prev == null && r.next != null) { r.next.prev = null; r.next.prevMap = r.prevMap; if (r.prevMap != null) { r.prevMap.nextMap = r.next; r.next.checkEquivalenceWithLeftNeighbor(r.prevMap,RenderMolecule.ALL_DIRTY_BITS); } // Head of the rmList else { rmlist = r.next; rmlist.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS; } allMap.put(r.localToVworld, r.next); } // Update the maps and remove this entry from the map list else if (r.prev == null && r.next == null) { if (r.prevMap != null) { r.prevMap.nextMap = r.nextMap; } else { rmlist = r.nextMap; if (r.nextMap != null) { rmlist.dirtyAttrsAcrossRms = RenderMolecule.ALL_DIRTY_BITS; } } if (r.nextMap != null) { r.nextMap.prevMap = r.prevMap; if (r.prevMap != null) { r.nextMap.checkEquivalenceWithLeftNeighbor(r.prevMap,RenderMolecule.ALL_DIRTY_BITS); } } allMap.remove(r.localToVworld); } r.prev = null; r.next = null; r.prevMap = null; r.nextMap = null; return rmlist; } void markDlistAsDirty(RenderMolecule r) { if (r.primaryMoleculeType == RenderMolecule.DLIST_MOLECULE) { renderBin.addDirtyRenderMolecule(r); } else if (r.primaryMoleculeType == RenderMolecule.SEPARATE_DLIST_PER_RINFO_MOLECULE) { RenderAtomListInfo ra = r.primaryRenderAtomList; while (ra != null) { renderBin.addDlistPerRinfo.add(ra); ra = ra.next; } } } void decrActiveRenderMolecule() { numEditingRenderMolecules--; if (numEditingRenderMolecules == 0) { // if number of editing renderMolecules goes to 0, // inform the shaderBin that this textureBin goes to // zombie state shaderBin.decrActiveTextureBin(); } } void incrActiveRenderMolecule() { if (numEditingRenderMolecules == 0) { // if this textureBin is in zombie state, inform // the shaderBin that this textureBin is activated again. shaderBin.incrActiveTextureBin(); } numEditingRenderMolecules++; } }