javax.media.j3d.TextureBin Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java3d-core Show documentation
Show all versions of java3d-core Show documentation
Java3D Core And Java3D Util Libraries
The newest version!
/*
* 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++;
}
}