/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.render.jsr231;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import javax.media.opengl.GL;
import org.jagatoo.logging.ProfileTimer;
import org.jagatoo.opengl.enums.CompareFunction;
import org.jagatoo.opengl.enums.TextureCompareMode;
import org.jagatoo.opengl.enums.TextureFilter;
import org.jagatoo.opengl.enums.TextureFormat;
import org.jagatoo.opengl.enums.TextureImageInternalFormat;
import org.jagatoo.opengl.enums.TextureMode;
import org.jagatoo.opengl.enums.TextureType;
import org.openmali.types.twodee.Rect2i;
import org.openmali.vecmath2.Colorf;
import org.xith3d.render.CanvasPeer;
import org.xith3d.render.OpenGLCapabilities;
import org.xith3d.render.OpenGLInfo;
import org.xith3d.render.OpenGLStatesCache;
import org.xith3d.render.OpenGlExtensions;
import org.xith3d.render.RenderOptions;
import org.xith3d.render.RenderPeer;
import org.xith3d.render.SceneGraphOpenGLReference;
import org.xith3d.render.SceneGraphOpenGLReferences;
import org.xith3d.render.RenderPeer.RenderMode;
import org.xith3d.render.preprocessing.RenderAtom;
import org.xith3d.render.states.StateUnit;
import org.xith3d.render.states.units.StateUnitPeer;
import org.xith3d.render.states.units.TextureUnitStateUnit;
import org.xith3d.scenegraph.Appearance;
import org.xith3d.scenegraph.GlobalOptions;
import org.xith3d.scenegraph.TextureImage;
import org.xith3d.scenegraph.TextureImage2D;
import org.xith3d.scenegraph.TextureImage3D;
import org.xith3d.scenegraph.ProjectiveTextureUnit;
import org.xith3d.scenegraph.Shape3D;
import org.xith3d.scenegraph.TexCoordGeneration;
import org.xith3d.scenegraph.Texture;
import org.xith3d.scenegraph.Texture2D;
import org.xith3d.scenegraph.Texture3D;
import org.xith3d.scenegraph.TextureAttributes;
import org.xith3d.scenegraph.TextureCubeMap;
import org.xith3d.scenegraph.TextureUnit;
import org.xith3d.scenegraph.Transform3D;
import org.xith3d.scenegraph.View;
import org.xith3d.scenegraph._SG_PrivilegedAccess;
import org.xith3d.utility.logging.X3DLog;
import com.sun.opengl.util.BufferUtil;
/**
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
*/
public class TextureUnitStateUnitPeer implements StateUnitPeer
{
private static final IntBuffer tmpIntBuffer = BufferUtil.newIntBuffer( 1 );
private static SceneGraphOpenGLReferences.Provider textureNameProvider = new SceneGraphOpenGLReferences.Provider()
{
public SceneGraphOpenGLReference newReference( CanvasPeer canvasPeer, SceneGraphOpenGLReferences references, int numNamesPerContext )
{
return ( new SceneGraphOpenGLReference( canvasPeer, references, numNamesPerContext )
{
@Override
public void prepareObjectForDestroy()
{
SceneGraphOpenGLReference ref = getReferences().removeReference( getContext().getCanvasID() );
( (CanvasPeerImplBase)getContext() ).addDestroyableObject( ref );
}
@Override
public void destroyObject( int index, int name )
{
final GL gl = ( (CanvasPeerImplBase)getContext() ).getGL();
tmpIntBuffer.clear();
tmpIntBuffer.put( name ).rewind();
gl.glDeleteTextures( 1, tmpIntBuffer );
}
} );
}
};
private static final FloatBuffer tmpBorderColor = BufferUtil.newFloatBuffer( 4 );
private static final FloatBuffer texBlendColor = BufferUtil.newFloatBuffer( 4 );
private static final FloatBuffer tmpPlaneBuffer = BufferUtil.newFloatBuffer( 4 );
private static final ByteBuffer textureDataBuffer = BufferUtil.newByteBuffer( 512 * 512 * 4 );
private static final FloatBuffer DEFAULT_TEXTURE_BLEND_COLOR = BufferUtil.newFloatBuffer( 4 );
static
{
DEFAULT_TEXTURE_BLEND_COLOR.put( 0, 0f );
DEFAULT_TEXTURE_BLEND_COLOR.put( 1, 0f );
DEFAULT_TEXTURE_BLEND_COLOR.put( 2, 0f );
DEFAULT_TEXTURE_BLEND_COLOR.put( 3, 1f );
DEFAULT_TEXTURE_BLEND_COLOR.rewind();
}
public static final int getMaxAnisotropicLevel( GL gl )
{
if ( !OpenGlExtensions.GL_EXT_texture_filter_anisotropic )
return ( 0 );
FloatBuffer buffer = BufferUtil.newFloatBuffer( 16 );
buffer.rewind();
gl.glGetFloatv( GL.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, buffer );
return ( (int)buffer.get( 0 ) );
}
public static final void selectServerTextureUnit( GL gl, int unit, OpenGLStatesCache statesCache, boolean force )
{
if ( ( statesCache.enabled && statesCache.currentServerTextureUnit == unit ) && !force )
return;
X3DLog.debug( "Activating (server) texture unit ", unit );
final int glUnit = GL.GL_TEXTURE0 + unit;
gl.glActiveTexture( glUnit );
statesCache.currentServerTextureUnit = unit;
}
public static final int translateInternalFormat( TextureFormat format, TextureImageInternalFormat internalFormat, int depthBuffersize )
{
if ( format == TextureFormat.DEPTH )
{
if ( depthBuffersize == 16 )
internalFormat = TextureImageInternalFormat.DEPTH16;
else if ( depthBuffersize == 24 )
internalFormat = TextureImageInternalFormat.DEPTH24;
else if ( depthBuffersize == 32 )
internalFormat = TextureImageInternalFormat.DEPTH32;
}
return ( internalFormat.toOpenGL() );
}
private static final int getCubeMapFace( int i )
{
/*
switch ( i )
{
case 0:
return ( GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X );
case 1:
return ( GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X );
case 2:
return ( GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y );
case 3:
return ( GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y );
case 4:
return ( GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z );
case 5:
return ( GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z );
default:
Error e = new Error( "Unrecognized cube map face: " + i );
X3DLog.print( e );
throw e );
}
*/
return ( GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i );
}
private static final void applyTextureAttachedAttributes( GL gl, Texture texture, boolean mipmapping, OpenGLCapabilities glCaps )
{
final TextureType texType = texture.getType();
//if ( ( type == Texture.Type.TEXTURE_2D ) || ( type == Texture.Type.TEXTURE_1D ) )
final int glTexType = texType.toOpenGL();
/*
* Setup texture boundary...
*/
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_WRAP_S, texture.getBoundaryModeS().toOpenGL() );
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_WRAP_T, texture.getBoundaryModeT().toOpenGL() );
if ( texType == TextureType.TEXTURE_3D )
{
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_WRAP_R, ( (Texture3D)texture ).getBoundaryModeR().toOpenGL() );
}
// boundary color
texture.getBoundaryColor().writeToBuffer( true, tmpBorderColor, true, true );
gl.glTexParameterfv( glTexType, GL.GL_TEXTURE_BORDER_COLOR, tmpBorderColor );
/*
* Setup texture filters...
*/
TextureFilter filter = texture.getFilter();
if ( filter == null )
{
filter = GlobalOptions.getInstance().getTextureFilter();
}
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_MAG_FILTER, filter.getOpenGLMagFilter() );
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_MIN_FILTER, filter.getOpenGLMinFilter(mipmapping) );
if ( OpenGlExtensions.GL_EXT_texture_filter_anisotropic )
{
int aniso = filter.getAnisotropicLevel();
int maxAniso = glCaps.getMaxAnisotropicLevel();
if ( aniso > maxAniso )
aniso = maxAniso;
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso );
}
}
private static final int defineTexture( GL gl, int unit, Texture texture, CanvasPeer canvasPeer, int depthBufferSize, OpenGLStatesCache statesCache )
{
final int numMipmaps = texture.getImagesCount();
if ( numMipmaps == 0 )
{
X3DLog.debug( "Found texture without images. Skipping!" );
return ( -1 );
}
/*
if ( texture instanceof Texture2D )
{
if ( ( (Texture2D)texture ).hasTextureCanvas() )
{
_SG_PrivilegedAccess.notifyDrawCallbacks( ( (Texture2D)texture ).getTextureCanvas(), nanoTime );
}
}
*/
SceneGraphOpenGLReference openGLRef = texture.getOpenGLReferences().getReference( canvasPeer, textureNameProvider );
int textureId = openGLRef.getName();
if ( textureId == -1 )
{
tmpIntBuffer.clear();
gl.glGenTextures( 1, tmpIntBuffer );
textureId = tmpIntBuffer.get( 0 );
openGLRef.setName( textureId );
}
final int glTexType = texture.getType().toOpenGL();
gl.glBindTexture( glTexType, textureId );
statesCache.currentBoundTexture[ unit ] = texture;
switch ( glTexType )
{
case GL.GL_TEXTURE_2D:
{
TextureImage2D image = null;
int format = 0;
int internalFormat = 0;
int border = 0; // !!! what does this do? !!!
for ( int level = 0; level < numMipmaps; level++ )
{
image = (TextureImage2D)texture.getImage( level );
if ( image.hasData() )
{
format = image.getFormat().toOpenGL();
internalFormat = translateInternalFormat( texture.getFormat(), image.getInternalFormat(), depthBufferSize );
ByteBuffer buff;
if ( image.getDataBuffer() != null )
{
buff = image.getDataBuffer();
}
else if ( image.getDataSize() > textureDataBuffer.capacity() )
{
buff = BufferUtil.newByteBuffer( image.getDataSize() );
image.getData( buff );
}
else
{
buff = textureDataBuffer;
image.getData( buff );
}
if ( image.isCompressed() )
{
gl.glCompressedTexImage2D( glTexType, level, internalFormat, image.getWidth() + 2 * border, image.getHeight() + 2 * border, border, buff.limit(), buff );
}
else
{
gl.glTexImage2D( glTexType, level, internalFormat, image.getWidth(), image.getHeight(), border, format, GL.GL_UNSIGNED_BYTE, buff );
}
if ( texture.isMarkedAsLocalDataToBeFreed() )
{
image.freeLocalData();
RenderPeerImpl.setGCRequested();
}
}
}
}
break;
case GL.GL_TEXTURE_3D:
{
TextureImage3D image = null;
int format = 0;
int internalFormat = 0;
int border = texture.getBoundaryWidth();
int depth = 0;
for ( int level = 0; level < numMipmaps; level++ )
{
image = (TextureImage3D)texture.getImage( level );
internalFormat = translateInternalFormat( texture.getFormat(), image.getInternalFormat(), depthBufferSize );
depth = image.getDepth();
ByteBuffer buff = image.getDataBuffer();
if ( image.isCompressed() )
{
gl.glCompressedTexImage3D( glTexType, level, internalFormat, image.getWidth() + 2 * border, image.getHeight() + 2 * border, depth, border, buff.limit(), buff );
}
else
{
format = image.getFormat().toOpenGL();
gl.glTexImage3D( glTexType, level, internalFormat, image.getWidth() + 2 * border, image.getHeight() + 2 * border, depth, border, format, GL.GL_UNSIGNED_BYTE, buff );
}
}
}
break;
case GL.GL_TEXTURE_CUBE_MAP:
{
// if ((texType == GL.GL_TEXTURE_CUBE_MAP) &&
// (OpenGlExtensions.GL_texture_cube_map)) {
for ( int i = 0; i < 6; i++ )
{
TextureImage image;
int face = 0;
int format;
int internalFormat;
final int border = 0; // !!! what does this do? !!!
for ( int level = 0; level < numMipmaps; level++ )
{
image = ( (TextureCubeMap)texture ).getImage( level, i );
format = image.getFormat().toOpenGL();
internalFormat = translateInternalFormat( texture.getFormat(), image.getInternalFormat(), depthBufferSize );
face = getCubeMapFace( i );
ByteBuffer buff;
if ( image instanceof TextureImage2D )
{
final TextureImage2D image2D = (TextureImage2D)image;
if ( image2D.getDataBuffer() != null )
{
buff = image2D.getDataBuffer();
}
else if ( image2D.getDataSize() > textureDataBuffer.capacity() )
{
buff = BufferUtil.newByteBuffer( image2D.getDataSize() );
image2D.getData( buff );
}
else
{
buff = textureDataBuffer;
image2D.getData( buff );
}
}
else if ( image instanceof TextureImage3D )
{
final TextureImage3D image3D = (TextureImage3D)image;
buff = image3D.getDataBuffer();
}
else
{
throw new Error( "Unknown TextureImage type " + image.getClass() );
}
if ( image.isCompressed() )
{
gl.glCompressedTexImage2D( face, level, internalFormat, image.getWidth() + 2 * border, image.getHeight() + 2 * border, border, buff.limit(), buff );
}
else
{
gl.glTexImage2D( face, level, internalFormat, image.getWidth(), image.getHeight(), border, format, GL.GL_UNSIGNED_BYTE, buff );
}
}
}
}
break;
}
return ( textureId );
}
private static final void updateTexture( GL gl, Texture2D texture )
{
/*
if ( texture.hasTextureCanvas() )
{
_SG_PrivilegedAccess.notifyDrawCallbacks( texture.getTextureCanvas() );
}
*/
if ( !texture.hasUpdateList() )
return;
final int numLevel = texture.getImagesCount();
for ( int level = 0; level < numLevel; level++ )
{
final TextureImage2D image = (TextureImage2D)texture.getImage( level );
final ArrayList< Rect2i > list = image.getUpdateList();
if ( !list.isEmpty() )
{
final int format = image.getFormat().toOpenGL();
final int listSize = list.size();
for ( int i = 0; i < listSize; i++ )
{
Rect2i r = list.get( i );
gl.glPixelStorei( GL.GL_UNPACK_ROW_LENGTH, image.getWidth() );
gl.glPixelStorei( GL.GL_UNPACK_SKIP_PIXELS, r.getLeft() );
gl.glPixelStorei( GL.GL_UNPACK_SKIP_ROWS, r.getTop() );
ByteBuffer buff;
if ( image.getDataBuffer() != null )
{
buff = image.getDataBuffer();
}
else if ( image.getDataSize() > textureDataBuffer.capacity() )
{
buff = BufferUtil.newByteBuffer( image.getDataSize() );
image.getData( buff );
}
else
{
buff = textureDataBuffer;
image.getData( buff );
}
// define the sub image
gl.glTexSubImage2D( GL.GL_TEXTURE_2D, level, r.getLeft(), r.getTop(), r.getWidth(), r.getHeight(), format, GL.GL_UNSIGNED_BYTE, buff );
}
image.clearUpdateList();
gl.glPixelStorei( GL.GL_UNPACK_ROW_LENGTH, 0 );
gl.glPixelStorei( GL.GL_UNPACK_SKIP_PIXELS, 0 );
gl.glPixelStorei( GL.GL_UNPACK_SKIP_ROWS, 0 );
}
}
texture.setHasUpdateList( false );
}
private static final int bindTexture( GL gl, OpenGLStatesCache statesCache, Texture texture, int unit, CanvasPeer canvasPeer, int depthBuffersize )
{
final TextureType texType = texture.getType();
final boolean tex2DEnabled;
final boolean tex3DEnabled;
final boolean texCMEnabled;
switch ( texType )
{
case TEXTURE_CUBE_MAP:
tex2DEnabled = false;
tex3DEnabled = false;
texCMEnabled = true;
break;
case TEXTURE_3D:
tex2DEnabled = false;
tex3DEnabled = true;
texCMEnabled = false;
break;
case TEXTURE_2D:
default:
tex2DEnabled = true;
tex3DEnabled = false;
texCMEnabled = false;
break;
}
if ( tex2DEnabled && ( !statesCache.enabled || !statesCache.texture2DEnabled[ unit ] ) )
gl.glEnable( GL.GL_TEXTURE_2D );
else if ( !tex2DEnabled && ( !statesCache.enabled || statesCache.texture2DEnabled[ unit ] ) )
gl.glDisable( GL.GL_TEXTURE_2D );
if ( tex3DEnabled && ( !statesCache.enabled || !statesCache.texture3DEnabled[ unit ] ) )
gl.glEnable( GL.GL_TEXTURE_3D );
else if ( !tex3DEnabled && ( !statesCache.enabled || statesCache.texture3DEnabled[ unit ] ) )
gl.glDisable( GL.GL_TEXTURE_3D );
if ( texCMEnabled && ( !statesCache.enabled || !statesCache.textureCMEnabled[ unit ] ) )
gl.glEnable( GL.GL_TEXTURE_CUBE_MAP );
else if ( !texCMEnabled && ( !statesCache.enabled || statesCache.textureCMEnabled[ unit ] ) )
gl.glDisable( GL.GL_TEXTURE_CUBE_MAP );
statesCache.texture2DEnabled[ unit ] = tex2DEnabled;
statesCache.texture3DEnabled[ unit ] = tex3DEnabled;
statesCache.textureCMEnabled[ unit ] = texCMEnabled;
SceneGraphOpenGLReference openGLRef = texture.getOpenGLReferences().getReference( canvasPeer, textureNameProvider );
int texHandle = openGLRef.getName();
if ( texture.isDirty() )
{
if ( texHandle != -1 )
{
tmpIntBuffer.clear();
tmpIntBuffer.put( texHandle ).flip();
gl.glDeleteTextures( 1, tmpIntBuffer );
texHandle = openGLRef.deleteName();
}
_SG_PrivilegedAccess.setDirty( texture, false );
}
if ( ( texHandle != -1 ) && ( !texture.hasSizeChanged() ) )
{
//X3DLog.debug( "Already cached, so binding texture" );
gl.glBindTexture( texType.toOpenGL(), texHandle ); // TODO: cache in OpenGLStatesCache?
statesCache.currentBoundTexture[ unit ] = texture;
if ( texType == TextureType.TEXTURE_2D )
{
updateTexture( gl, (Texture2D)texture );
}
}
else
{
texHandle = defineTexture( gl, unit, texture, canvasPeer, depthBuffersize, statesCache );
_SG_PrivilegedAccess.resetSizeChanged( texture );
}
return ( texHandle );
}
private static final int setTextureState( GL gl, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, Texture texture, int unit, boolean texChanged, CanvasPeer canvasPeer, int depthBuffersize )
{
final int texHandle = bindTexture( gl, statesCache, texture, unit, canvasPeer, depthBuffersize );
// if ( texChanged )
// {
applyTextureAttachedAttributes( gl, texture, ( texture.getImagesCount() > 1 ), glCaps );
_SG_PrivilegedAccess.setChanged( texture, false );
// }
return ( texHandle );
}
private static final void disableTextureState( GL gl, OpenGLStatesCache statesCache, Texture texture, int unit, boolean texChanged )
{
selectServerTextureUnit( gl, unit, statesCache, false );
if ( !statesCache.enabled || statesCache.texture2DEnabled[ unit ] )
{
gl.glDisable( GL.GL_TEXTURE_2D );
statesCache.texture2DEnabled[ unit ] = false;
}
if ( !statesCache.enabled || statesCache.texture3DEnabled[ unit ] )
{
gl.glDisable( GL.GL_TEXTURE_3D );
statesCache.texture3DEnabled[ unit ] = false;
}
if ( !statesCache.enabled || statesCache.textureCMEnabled[ unit ] )
{
gl.glDisable( GL.GL_TEXTURE_CUBE_MAP );
statesCache.textureCMEnabled[ unit ] = false;
}
if ( texChanged )
{
_SG_PrivilegedAccess.setChanged( texture, false );
}
}
protected static final int setTextureState2( GL gl, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, Texture texture, int unit, boolean texChanged, CanvasPeer canvasPeer, int depthBuffersize )
{
if ( ( texture == null ) || !texture.isEnabled() )
{
disableTextureState( gl, statesCache, texture, unit, texChanged );
return ( -1 );
}
return ( setTextureState( gl, glCaps, statesCache, texture, unit, texChanged, canvasPeer, depthBuffersize ) );
}
private static final void setCompareMode( GL gl, OpenGLInfo glInfo, int glTexType, TextureCompareMode mode )
{
if ( glInfo.getNormalizedVersion() >= OpenGLInfo.NORM_VERSION_1_4 )
{
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_COMPARE_MODE, mode.toOpenGL() );
}
else if ( OpenGlExtensions.ARB_shadow )
{
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_COMPARE_MODE_ARB, mode.toOpenGL() );
}
}
private static final void setCompareFunc( GL gl, OpenGLInfo glInfo, int glTexType, CompareFunction func )
{
if ( glInfo.getNormalizedVersion() >= OpenGLInfo.NORM_VERSION_1_4 )
{
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_COMPARE_FUNC, func.toOpenGL() );
}
else if ( OpenGlExtensions.ARB_shadow )
{
gl.glTexParameteri( glTexType, GL.GL_TEXTURE_COMPARE_FUNC_ARB, func.toOpenGL() );
}
}
private static final void setTextureMatrix( GL gl, Transform3D trans )
{
gl.glMatrixMode( GL.GL_TEXTURE );
gl.glLoadMatrixf( _SG_PrivilegedAccess.getFloatBuffer( trans, true ) );
gl.glMatrixMode( GL.GL_MODELVIEW );
}
private static final void setIdentityTexMat( GL gl )
{
gl.glMatrixMode( GL.GL_TEXTURE );
gl.glLoadIdentity();
gl.glMatrixMode( GL.GL_MODELVIEW );
}
private void setTextureAttributes( GL gl, int unit, TextureType texType, TextureAttributes ta, OpenGLInfo glInfo, OpenGLStatesCache statesCache )
{
if ( ( ta == null ) || ( ta.getTextureTransform() == null ) )
{
if ( !statesCache.enabled || statesCache.currentTextureMatrix[ unit ] != null )
{
setIdentityTexMat( gl );
statesCache.currentTextureMatrix[ unit ] = null;
}
}
if ( ta == null )
{
if ( !statesCache.enabled || statesCache.currentTextureMode[ unit ] != TextureMode.MODULATE )
{
gl.glTexEnvi( GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE );
statesCache.currentTextureMode[ unit ] = TextureMode.MODULATE;
}
return;
}
final Transform3D texTrans = ta.getTextureTransform();
if ( texTrans != null )
{
if ( ( !statesCache.enabled || statesCache.currentTextureMatrix[ unit ] != texTrans ) || texTrans.isChanged() )
{
setTextureMatrix( gl, texTrans );
statesCache.currentTextureMatrix[ unit ] = texTrans;
_SG_PrivilegedAccess.setChanged( texTrans, false );
}
}
final TextureMode textureMode = ta.getTextureMode();
if ( !statesCache.enabled || statesCache.currentTextureMode[ unit ] != textureMode )
{
gl.glTexEnvi( GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, textureMode.toOpenGL() );
statesCache.currentTextureMode[ unit ] = textureMode;
}
if ( textureMode == TextureMode.COMBINE )
{
Colorf tbc = ta.getTextureBlendColor();
if ( tbc == null )
{
if ( !statesCache.enabled || statesCache.currentTextureBlendColor[ unit ] != null )
{
gl.glTexEnvfv( GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_COLOR, DEFAULT_TEXTURE_BLEND_COLOR );
statesCache.currentTextureBlendColor[ unit ] = null;
}
}
else
{
if ( ( !statesCache.enabled || statesCache.currentTextureBlendColor[ unit ] != tbc ) || tbc.isDirty() )
{
/*
texBlendColor.put( 0, tbc.getRed() );
texBlendColor.put( 1, tbc.getGreen() );
texBlendColor.put( 2, tbc.getBlue() );
texBlendColor.put( 3, tbc.getAlpha() );
*/
tbc.writeToBuffer( true, texBlendColor, true, true );
gl.glTexEnvfv( GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_COLOR, texBlendColor );
statesCache.currentTextureBlendColor[ unit ] = tbc;
tbc.setClean();
}
}
// setup rgb
if ( !statesCache.enabled || statesCache.currentCombineMode_RGB[ unit ] != ta.getCombineRGBMode() )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_COMBINE_RGB, ta.getCombineRGBMode().toOpenGL() );
statesCache.currentCombineMode_RGB[ unit ] = ta.getCombineRGBMode();
}
if ( !statesCache.enabled || statesCache.currentCombineMode_Alpha[ unit ] != ta.getCombineAlphaMode() )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_COMBINE_ALPHA, ta.getCombineAlphaMode().toOpenGL() );
statesCache.currentCombineMode_Alpha[ unit ] = ta.getCombineAlphaMode();
}
if ( !statesCache.enabled || statesCache.currentCombineSource0_RGB[ unit ] != ta.getCombineRGBSource( 0 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_SOURCE0_RGB, ta.getCombineRGBSource( 0 ).toOpenGL() );
statesCache.currentCombineSource0_RGB[ unit ] = ta.getCombineRGBSource( 0 );
}
if ( !statesCache.enabled || statesCache.currentCombineSource0_Alpha[ unit ] != ta.getCombineAlphaSource( 0 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_SOURCE0_ALPHA, ta.getCombineAlphaSource( 0 ).toOpenGL() );
statesCache.currentCombineSource0_Alpha[ unit ] = ta.getCombineAlphaSource( 0 );
}
if ( !statesCache.enabled || statesCache.currentCombineFunction0_RGB[ unit ] != ta.getCombineRGBFunction( 0 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_OPERAND0_RGB, ta.getCombineRGBFunction( 0 ).toOpenGL() );
statesCache.currentCombineFunction0_RGB[ unit ] = ta.getCombineRGBFunction( 0 );
}
if ( !statesCache.enabled || statesCache.currentCombineFunction0_Alpha[ unit ] != ta.getCombineAlphaFunction( 0 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_OPERAND0_ALPHA, ta.getCombineAlphaFunction( 0 ).toOpenGL() );
statesCache.currentCombineFunction0_Alpha[ unit ] = ta.getCombineAlphaFunction( 0 );
}
if ( !statesCache.enabled || statesCache.currentCombineSource1_RGB[ unit ] != ta.getCombineRGBSource( 1 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_SOURCE1_RGB, ta.getCombineRGBSource( 1 ).toOpenGL() );
statesCache.currentCombineSource1_RGB[ unit ] = ta.getCombineRGBSource( 1 );
}
if ( !statesCache.enabled || statesCache.currentCombineSource1_Alpha[ unit ] != ta.getCombineAlphaSource( 1 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_SOURCE1_ALPHA, ta.getCombineAlphaSource( 1 ).toOpenGL() );
statesCache.currentCombineSource1_Alpha[ unit ] = ta.getCombineAlphaSource( 1 );
}
if ( !statesCache.enabled || statesCache.currentCombineFunction0_RGB[ unit ] != ta.getCombineRGBFunction( 1 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_OPERAND1_RGB, ta.getCombineRGBFunction( 1 ).toOpenGL() );
statesCache.currentCombineFunction0_RGB[ unit ] = ta.getCombineRGBFunction( 1 );
}
if ( !statesCache.enabled || statesCache.currentCombineFunction0_Alpha[ unit ] != ta.getCombineAlphaFunction( 1 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_OPERAND1_ALPHA, ta.getCombineAlphaFunction( 1 ).toOpenGL() );
statesCache.currentCombineFunction0_Alpha[ unit ] = ta.getCombineAlphaFunction( 1 );
}
if ( !statesCache.enabled || statesCache.currentCombineSource2_RGB[ unit ] != ta.getCombineRGBSource( 2 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_SOURCE2_RGB, ta.getCombineRGBSource( 2 ).toOpenGL() );
statesCache.currentCombineSource2_RGB[ unit ] = ta.getCombineRGBSource( 2 );
}
if ( !statesCache.enabled || statesCache.currentCombineSource2_Alpha[ unit ] != ta.getCombineAlphaSource( 2 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_SOURCE2_ALPHA, ta.getCombineAlphaSource( 2 ).toOpenGL() );
statesCache.currentCombineSource2_Alpha[ unit ] = ta.getCombineAlphaSource( 2 );
}
if ( !statesCache.enabled || statesCache.currentCombineFunction0_RGB[ unit ] != ta.getCombineRGBFunction( 2 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_OPERAND2_RGB, ta.getCombineRGBFunction( 2 ).toOpenGL() );
statesCache.currentCombineFunction0_RGB[ unit ] = ta.getCombineRGBFunction( 2 );
}
if ( !statesCache.enabled || statesCache.currentCombineFunction0_Alpha[ unit ] != ta.getCombineAlphaFunction( 2 ) )
{
gl.glTexEnvf( GL.GL_TEXTURE_ENV, GL.GL_OPERAND2_ALPHA, ta.getCombineAlphaFunction( 2 ).toOpenGL() );
statesCache.currentCombineFunction0_Alpha[ unit ] = ta.getCombineAlphaFunction( 2 );
}
if ( !statesCache.enabled || statesCache.currentCombineRGBScale[ unit ] != ta.getCombineRGBScale() )
{
gl.glTexEnvi( GL.GL_TEXTURE_ENV, GL.GL_RGB_SCALE, ta.getCombineRGBScale() );
statesCache.currentCombineRGBScale[ unit ] = ta.getCombineRGBScale();
}
}
if ( ( texType == TextureType.TEXTURE_2D ) || ( texType == TextureType.TEXTURE_1D ) )
{
final int glTexType = texType.toOpenGL();
if ( !statesCache.enabled || statesCache.currentCompareMode[ unit ] != ta.getCompareMode() )
{
setCompareMode( gl, glInfo, glTexType, ta.getCompareMode() );
statesCache.currentCompareMode[ unit ] = ta.getCompareMode();
}
if ( !statesCache.enabled || statesCache.currentCompareFunc[ unit ] != ta.getCompareFunction() )
{
setCompareFunc( gl, glInfo, glTexType, ta.getCompareFunction() );
statesCache.currentCompareFunc[ unit ] = ta.getCompareFunction();
}
}
}
private final void updateProjectiveTexture( Shape3D shape, int tuIndex, CanvasPeerImplBase canvas, long frameId )
{
final Appearance app = shape.getAppearance();
if ( app == null )
return;
final TextureUnit tu = app.getTextureUnit( tuIndex );
if ( !( tu instanceof ProjectiveTextureUnit ) )
return;
final ProjectiveTextureUnit projTU = (ProjectiveTextureUnit)tu;
final float viewportAspect;
if ( canvas.getCurrentViewport() == null )
viewportAspect = canvas.getDisplayMode().getAspect();
else
viewportAspect = canvas.getCurrentViewport().getAspect();
projTU.update( viewportAspect, frameId );
}
private static final void setupTextureCoordsGeneration( GL gl, TexCoordGeneration texGen )
{
final int glGenMode = texGen.getGenMode().toOpenGL();
int glPlaneName = GL.GL_EYE_PLANE;
FloatBuffer planeBuf = null;
switch ( texGen.getGenMode() )
{
case OBJECT_LINEAR:
glPlaneName = GL.GL_OBJECT_PLANE;
planeBuf = tmpPlaneBuffer;
break;
case EYE_LINEAR:
glPlaneName = GL.GL_EYE_PLANE;
planeBuf = tmpPlaneBuffer;
break;
case SPHERE_MAP:
break;
case NORMAL_MAP:
break;
case REFLECTION_MAP:
break;
default:
throw new Error( "Unsupported Texture-generation-mode " + texGen.getGenMode() );
}
switch ( texGen.getFormat() )
{
case TEXTURE_COORDINATES_1:
gl.glTexGeni( GL.GL_S, GL.GL_TEXTURE_GEN_MODE, glGenMode );
if ( planeBuf != null )
{
texGen.getPlaneS().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_S, glPlaneName, planeBuf );
}
break;
case TEXTURE_COORDINATES_2:
gl.glTexGeni( GL.GL_S, GL.GL_TEXTURE_GEN_MODE, glGenMode );
gl.glTexGeni( GL.GL_T, GL.GL_TEXTURE_GEN_MODE, glGenMode );
if ( planeBuf != null )
{
texGen.getPlaneS().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_S, glPlaneName, planeBuf );
texGen.getPlaneT().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_T, glPlaneName, planeBuf );
}
break;
case TEXTURE_COORDINATES_3:
gl.glTexGeni( GL.GL_S, GL.GL_TEXTURE_GEN_MODE, glGenMode );
gl.glTexGeni( GL.GL_T, GL.GL_TEXTURE_GEN_MODE, glGenMode );
gl.glTexGeni( GL.GL_R, GL.GL_TEXTURE_GEN_MODE, glGenMode );
if ( planeBuf != null )
{
texGen.getPlaneS().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_S, glPlaneName, planeBuf );
texGen.getPlaneT().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_T, glPlaneName, planeBuf );
texGen.getPlaneR().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_R, glPlaneName, planeBuf );
}
break;
case TEXTURE_COORDINATES_4:
gl.glTexGeni( GL.GL_S, GL.GL_TEXTURE_GEN_MODE, glGenMode );
gl.glTexGeni( GL.GL_T, GL.GL_TEXTURE_GEN_MODE, glGenMode );
gl.glTexGeni( GL.GL_R, GL.GL_TEXTURE_GEN_MODE, glGenMode );
gl.glTexGeni( GL.GL_Q, GL.GL_TEXTURE_GEN_MODE, glGenMode );
if ( planeBuf != null )
{
texGen.getPlaneS().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_S, glPlaneName, planeBuf );
texGen.getPlaneT().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_T, glPlaneName, planeBuf );
texGen.getPlaneR().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_R, glPlaneName, planeBuf );
texGen.getPlaneQ().writeToBuffer( planeBuf, true, true );
gl.glTexGenfv( GL.GL_Q, glPlaneName, planeBuf );
}
break;
}
}
private static final void applyTexCoordGenStates( GL gl , int unit, int statesMask, OpenGLStatesCache statesCache )
{
if ( ( statesMask & 1 ) != 0 )
{
if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 1 ) == 0 )
{
gl.glEnable( GL.GL_TEXTURE_GEN_S );
statesCache.texGenEnableMask[ unit ] |= 1;
}
}
else if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 1 ) != 0 )
{
gl.glDisable( GL.GL_TEXTURE_GEN_S );
statesCache.texGenEnableMask[ unit ] &= ~1;
}
if ( ( statesMask & 2 ) != 0 )
{
if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 2 ) == 0 )
{
gl.glEnable( GL.GL_TEXTURE_GEN_T );
statesCache.texGenEnableMask[ unit ] |= 2;
}
}
else if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 2 ) != 0 )
{
gl.glDisable( GL.GL_TEXTURE_GEN_T );
statesCache.texGenEnableMask[ unit ] &= ~2;
}
if ( ( statesMask & 4 ) != 0 )
{
if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 4 ) == 0 )
{
gl.glEnable( GL.GL_TEXTURE_GEN_R );
statesCache.texGenEnableMask[ unit ] |= 4;
}
}
else if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 4 ) != 0 )
{
gl.glDisable( GL.GL_TEXTURE_GEN_R );
statesCache.texGenEnableMask[ unit ] &= ~4;
}
if ( ( statesMask & 8 ) != 0 )
{
if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 8 ) == 0 )
{
gl.glEnable( GL.GL_TEXTURE_GEN_Q );
statesCache.texGenEnableMask[ unit ] |= 8;
}
}
else if ( !statesCache.enabled || ( statesCache.texGenEnableMask[ unit ] & 8 ) != 0 )
{
gl.glDisable( GL.GL_TEXTURE_GEN_Q );
statesCache.texGenEnableMask[ unit ] &= ~8;
}
}
public void apply( RenderAtom< ? > atom, StateUnit stateUnit, Object glObj, CanvasPeer canvasPeer, RenderPeer renderPeer, OpenGLCapabilities glCaps, View view, OpenGLStatesCache statesCache, RenderOptions options, long nanoTime, long nanoStep, RenderMode renderMode, long frameId )
{
if ( !options.isTextureMappingEnabled() || ( renderMode != RenderMode.NORMAL ) )
{
return;
}
ProfileTimer.startProfile( X3DLog.LOG_CHANNEL, "TextureUnitStateUnitPeer::apply()" );
final GL gl = (GL)glObj;
final TextureUnitStateUnit texStateUnit = (TextureUnitStateUnit)stateUnit;
final int unit = texStateUnit.getUnit();
// (yvg) We should not jump out of allowed number of texture units,
// because of this causes invalid behavior on some drivers
if ( unit >= glCaps.getMaxTextureUnits() )
{
ProfileTimer.endProfile();
return;
}
final Texture texture = texStateUnit.getTexture();
if ( texture == null )
{
disableTextureState( gl, statesCache, texture, unit, false );
statesCache.currentBoundTexture[ unit ] = texture;
ProfileTimer.endProfile();
return;
}
else if ( !texture.isEnabled() )
{
disableTextureState( gl, statesCache, texture, unit, texture.isChanged2() );
statesCache.currentBoundTexture[ unit ] = texture;
ProfileTimer.endProfile();
return;
}
boolean changed = texture.isChanged2();
if ( ( ( texture instanceof Texture2D ) && ( ( ( (Texture2D)texture ).hasTextureCanvas() && _SG_PrivilegedAccess.notifyDrawCallbacks( ( (Texture2D)texture ).getTextureCanvas(), nanoTime ) ) || ( (Texture2D)texture ).hasUpdateList() ) ) || ( !statesCache.enabled || statesCache.currentBoundTexture[ unit ] != texture ) || changed )
{
selectServerTextureUnit( gl, unit, statesCache, false );
// if this is multi texture then shape atom will handle it
setTextureState( gl, glCaps, statesCache, texture, unit, changed, canvasPeer, canvasPeer.getDepthBufferSize() );
statesCache.currentBoundTexture[ unit ] = texture;
//if ( texture != null )
// _SG_PrivilegedAccess.setChanged( texture, false );
}
final TextureAttributes texAttribs = texStateUnit.getTextureAttributes();
changed = texAttribs.isChanged();
boolean hasProjectiveTex = ( atom.getNode() instanceof Shape3D );
if ( hasProjectiveTex )
{
Appearance app = ( (Shape3D)atom.getNode() ).getAppearance();
if ( app == null )
hasProjectiveTex = false;
else
hasProjectiveTex = ( app.getTextureUnit( unit ) instanceof ProjectiveTextureUnit );
}
//if ( ( !statesCache.enabled || statesCache.currentTexAttribs[ unit ] != texAttribs ) || changed || hasProjectiveTex )
{
if ( hasProjectiveTex )
{
updateProjectiveTexture( (Shape3D)atom.getNode(), unit, (CanvasPeerImplBase)renderPeer.getCanvasPeer(), frameId );
}
selectServerTextureUnit( gl, unit, statesCache, false );
setTextureAttributes( gl, unit, texture.getType(), texAttribs, renderPeer.getCanvasPeer().getOpenGLInfo(), statesCache );
statesCache.currentTexAttribs[ unit ] = texAttribs;
if ( changed )
_SG_PrivilegedAccess.setChanged( texAttribs, false );
}
final TexCoordGeneration texCoordGen = texStateUnit.getTexCoordGeneration();
changed = texCoordGen.isChanged();
//if ( ( !statesCache.enabled || statesCache.currentTexCoordGen[ unit ] != texCoordGen ) || changed )
{
if ( /*( texCoordGen != null ) && */texCoordGen.isEnabled() )
{
ShapeAtomPeer.setMatrix( gl, view, atom.getNode().getWorldTransform(), atom.getNode().isBillboard(), false );
selectServerTextureUnit( gl, unit, statesCache, false );
setupTextureCoordsGeneration( gl, texCoordGen );
final int texGenMask = texCoordGen.getFormat().getBitMask();
if ( !statesCache.enabled || statesCache.texGenEnableMask[ unit ] != texGenMask )
{
applyTexCoordGenStates( gl, unit, texGenMask, statesCache );
}
}
else
{
if ( !statesCache.enabled || statesCache.texGenEnableMask[ unit ] != 0 )
{
selectServerTextureUnit( gl, unit, statesCache, false );
applyTexCoordGenStates( gl, unit, 0, statesCache );
}
}
statesCache.currentTexCoordGen[ unit ] = texCoordGen;
if (changed )
_SG_PrivilegedAccess.setChanged( texCoordGen, false );
}
ProfileTimer.endProfile();
}
}