/** * 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.lwjgl; import java.nio.FloatBuffer; import java.nio.IntBuffer; import org.jagatoo.geometry.GeomNioData; import org.jagatoo.logging.ProfileTimer; import org.jagatoo.opengl.enums.FaceCullMode; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.ARBVertexBufferObject; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; import org.openmali.vecmath2.Matrix4f; import org.xith3d.render.CanvasPeer; import org.xith3d.render.OpenGLCapabilities; import org.xith3d.render.OpenGLStatesCache; import org.xith3d.render.OpenGlExtensions; import org.xith3d.render.RenderAtomPeer; 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.preprocessing.ShapeAtom; import org.xith3d.scenegraph.Appearance; import org.xith3d.scenegraph.Billboard; import org.xith3d.scenegraph.GeomNioFloatData; import org.xith3d.scenegraph.GeomNioIntData; import org.xith3d.scenegraph.Geometry; import org.xith3d.scenegraph.GeometryStripArray; import org.xith3d.scenegraph.IndexedGeometryArray; import org.xith3d.scenegraph.IndexedGeometryStripArray; import org.xith3d.scenegraph.PolygonAttributes; import org.xith3d.scenegraph.Shape3D; import org.xith3d.scenegraph.Transform3D; import org.xith3d.scenegraph.View; import org.xith3d.scenegraph._SG_PrivilegedAccess; import org.xith3d.scenegraph.Geometry.Optimization; import org.xith3d.utility.logging.X3DLog; /** * Renders a single Shape3D. * * @author David Yazel * @author Marvin Froehlich (aka Qudus) */ public class ShapeAtomPeer extends RenderAtomPeer { private static enum VBOMode { ALWAYS, AUTO, NEVER } private static final int OPT_AUTO_MAX_FRAMES = 10; private static final int CARE_MAP_COORDINATES = 1; private static final int CARE_MAP_NORMALS = 2; private static final int CARE_MAP_COLORS = 4; private static final int CARE_MAP_TEXTURE_COORDS = 8; private static final int CARE_MAP_VERTEX_ATTRIBS = 16; private static final int CARE_MAP_INDICES = 32; protected static final int CARE_MAP_ALL = CARE_MAP_COORDINATES | CARE_MAP_NORMALS | CARE_MAP_COLORS | CARE_MAP_TEXTURE_COORDS | CARE_MAP_VERTEX_ATTRIBS | CARE_MAP_INDICES; /** * temporary buffer for setting texcoord generation plane */ private static final IntBuffer tmpIntBuffer = BufferUtils.createIntBuffer( 16 ); private static final IntBuffer tmpInt1Buffer = BufferUtils.createIntBuffer( 1 ); private static FloatBuffer float16 = BufferUtils.createFloatBuffer( 16 ); private static Transform3D lastTransform = null; private static SceneGraphOpenGLReferences.Provider vboNameProvider = 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 ) { tmpIntBuffer.clear(); tmpIntBuffer.put( 0, name ); if ( getContext().getOpenGLCapabilities().isMinVersion15() ) GL15.glDeleteBuffers( tmpIntBuffer ); else ARBVertexBufferObject.glDeleteBuffersARB( tmpIntBuffer ); } } ); } }; /** * Prepares this instance to render the next frame. */ public static final void reset() { lastTransform = null; } protected static final void setMatrix( View view, Transform3D transform, boolean ignoreRotation, boolean forced ) { if ( ( lastTransform == transform ) && !forced ) return; lastTransform = transform; // Combine WorldTransform and View (camera) transforms GL11.glLoadMatrix( _SG_PrivilegedAccess.getFloatBuffer( view.getModelViewTransform( false ), true ) ); if ( !ignoreRotation ) { GL11.glMultMatrix( _SG_PrivilegedAccess.getFloatBuffer( transform, true ) ); } else { final Matrix4f mat = transform.getMatrix4f(); float16.clear(); float16.put( 1.0f ); float16.put( 0.0f ); float16.put( 0.0f ); float16.put( mat.m30() ); float16.put( 0.0f ); float16.put( 1.0f ); float16.put( 0.0f ); float16.put( mat.m31() ); float16.put( 0.0f ); float16.put( 0.0f ); float16.put( 1.0f ); float16.put( mat.m32() ); float16.put( mat.m03() ); float16.put( mat.m13() ); float16.put( mat.m23() ); float16.put( mat.m33() ); float16.flip(); GL11.glMultMatrix( float16 ); } } private static final VBOMode getTexCoordVBOMode( Optimization optimization ) { switch ( optimization ) { case USE_VBOS: case USE_VBO_FOR_TEXTURE_COORDINATES: return ( VBOMode.ALWAYS ); case NONE: case USE_DISPLAY_LISTS: case USE_VBO_FOR_VERTEX_DATA: return ( VBOMode.NEVER ); case AUTO: default: return ( VBOMode.AUTO ); } } private static final VBOMode getGeomVBOMode( Optimization optimization ) { switch ( optimization ) { case USE_VBOS: case USE_VBO_FOR_VERTEX_DATA: return ( VBOMode.ALWAYS ); case NONE: case USE_DISPLAY_LISTS: case USE_VBO_FOR_TEXTURE_COORDINATES: return ( VBOMode.NEVER ); case AUTO: default: return ( VBOMode.AUTO ); } } private static final boolean vboModeToBool( VBOMode vboMode, GeomNioData data ) { switch ( vboMode ) { case ALWAYS: return ( true ); case NEVER: return ( false ); case AUTO: default: return ( _SG_PrivilegedAccess.getFramesSinceDirty( data ) >= OPT_AUTO_MAX_FRAMES ); } } private static final void bindArrayVBO( int vbo, OpenGLStatesCache statesCache, boolean glVBOsSupported, boolean arbVBOsSupported ) { if ( ( statesCache.enabled && statesCache.currentBoundArrayVBO == vbo ) || ( !glVBOsSupported && !arbVBOsSupported ) ) return; if ( glVBOsSupported ) GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, vbo ); else ARBVertexBufferObject.glBindBufferARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, vbo ); statesCache.currentBoundArrayVBO = vbo; } private static final void bindIndexVBO( int vbo, OpenGLStatesCache statesCache, boolean glVBOsSupported, boolean arbVBOsSupported ) { if ( ( statesCache.enabled && statesCache.currentBoundElementVBO == vbo ) || ( !glVBOsSupported && !arbVBOsSupported ) ) return; if ( glVBOsSupported ) GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, vbo ); else ARBVertexBufferObject.glBindBufferARB( ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, vbo ); statesCache.currentBoundElementVBO = vbo; } private static final void createAndBindVBO( CanvasPeer canvasPeer, GeomNioData data, int arrayType, OpenGLStatesCache statesCache, boolean glVBOsSupported, boolean arbVBOsSupported ) { final SceneGraphOpenGLReference openGLRef = ( data instanceof GeomNioFloatData ) ? ( (GeomNioFloatData)data ).getOpenGLReferences().getReference( canvasPeer, vboNameProvider ) : ( (GeomNioIntData)data ).getOpenGLReferences().getReference( canvasPeer, vboNameProvider ); // if the data is not currently cached then get a cache handle int cacheHandle = openGLRef.getName(); boolean newHandle = false; if ( cacheHandle == -1 ) { tmpInt1Buffer.clear(); if ( glVBOsSupported ) GL15.glGenBuffers( tmpInt1Buffer ); else if ( arbVBOsSupported ) ARBVertexBufferObject.glGenBuffersARB( tmpInt1Buffer ); cacheHandle = tmpInt1Buffer.get( 0 ); openGLRef.setName( cacheHandle ); newHandle = true; } // bind to the buffer if ( arrayType == 0 ) bindIndexVBO( cacheHandle, statesCache, glVBOsSupported, arbVBOsSupported ); else bindArrayVBO( cacheHandle, statesCache, glVBOsSupported, arbVBOsSupported ); // if the data is dirty update the buffer if ( _SG_PrivilegedAccess.isDirty( data ) || newHandle ) { if ( arrayType == 0 ) // index { final IntBuffer buffer = ((GeomNioIntData)data).getBuffer(); buffer.rewind(); if ( glVBOsSupported ) GL15.glBufferData( GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW ); else if ( arbVBOsSupported ) ARBVertexBufferObject.glBufferDataARB( ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, buffer, ARBVertexBufferObject.GL_STATIC_DRAW_ARB ); } else { final FloatBuffer buffer = ((GeomNioFloatData)data).getBuffer(); buffer.rewind(); if ( glVBOsSupported ) GL15.glBufferData( GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW ); else if ( arbVBOsSupported ) ARBVertexBufferObject.glBufferDataARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, buffer, ARBVertexBufferObject.GL_STATIC_DRAW_ARB ); } _SG_PrivilegedAccess.setDirty( data, false ); _SG_PrivilegedAccess.incrementFramesSinceDirty( data ); } } private static final void bindVertexArray( CanvasPeer canvasPeer, GeomNioData data, int elemSize, int stride, long offset, int index, int arrayType, OpenGLStatesCache statesCache, boolean glVBOsSupported, boolean arbVBOsSupported ) { final SceneGraphOpenGLReference openGLRef = ( data instanceof GeomNioFloatData ) ? ( (GeomNioFloatData)data ).getOpenGLReferences().getReference( canvasPeer, vboNameProvider ) : ( (GeomNioIntData)data ).getOpenGLReferences().getReference( canvasPeer, vboNameProvider ); int cacheHandle = openGLRef.getName(); if ( cacheHandle != -1 ) { tmpIntBuffer.clear(); tmpIntBuffer.put( cacheHandle ).flip(); if ( glVBOsSupported ) GL15.glDeleteBuffers( tmpIntBuffer ); else if ( arbVBOsSupported ) ARBVertexBufferObject.glDeleteBuffersARB( tmpIntBuffer ); cacheHandle = openGLRef.deleteName(); } bindArrayVBO( 0, statesCache, glVBOsSupported, arbVBOsSupported ); bindIndexVBO( 0, statesCache, glVBOsSupported, arbVBOsSupported ); final FloatBuffer dataBuffer = ((GeomNioFloatData)data).getBuffer(); dataBuffer.position( (int)( offset / 4L ) ); switch ( arrayType ) { case GL11.GL_VERTEX_ARRAY: GL11.glVertexPointer( elemSize, stride, dataBuffer ); break; case GL11.GL_NORMAL_ARRAY: GL11.glNormalPointer( stride, dataBuffer ); break; case GL11.GL_COLOR_ARRAY: GL11.glColorPointer( elemSize, stride, dataBuffer ); break; case GL11.GL_TEXTURE_COORD_ARRAY: GL11.glTexCoordPointer( elemSize, stride, dataBuffer ); break; case GL20.GL_VERTEX_ATTRIB_ARRAY_POINTER: GL20.glVertexAttribPointer( index, elemSize, false, stride, dataBuffer ); break; } _SG_PrivilegedAccess.setDirty( data, false ); _SG_PrivilegedAccess.incrementFramesSinceDirty( data ); } /** * binds the geometry component to the proper vertex array and uses caching * if enabled and if the caching policy of the data deems it appropriate * * @return true, if a VBO is used */ private static final boolean bindGeometryComponent( CanvasPeer canvasPeer, OpenGLStatesCache statesCache, GeomNioData data, int elemSize, int stride, long offset, int index, int arrayType, VBOMode vboMode, Boolean useVBO, boolean glVBOsSupported, boolean arbVBOsSupported ) { if ( useVBO == null ) { useVBO = vboModeToBool( vboMode, data ); } useVBO = useVBO.booleanValue() && ( glVBOsSupported || arbVBOsSupported ); if ( useVBO.booleanValue() ) { if ( data != null ) { createAndBindVBO( canvasPeer, data, arrayType, statesCache, glVBOsSupported, arbVBOsSupported ); } // set the data pointer switch ( arrayType ) { case GL11.GL_VERTEX_ARRAY: GL11.glVertexPointer( elemSize, GL11.GL_FLOAT, stride, offset ); break; case GL11.GL_NORMAL_ARRAY: GL11.glNormalPointer( GL11.GL_FLOAT, stride, offset ); break; case GL11.GL_COLOR_ARRAY: GL11.glColorPointer( elemSize, GL11.GL_FLOAT, stride, offset ); break; case GL11.GL_TEXTURE_COORD_ARRAY: GL11.glTexCoordPointer( elemSize, GL11.GL_FLOAT, stride, offset ); break; case GL20.GL_VERTEX_ATTRIB_ARRAY_POINTER: GL20.glVertexAttribPointer( index, elemSize, GL11.GL_FLOAT, false, stride, offset ); break; } } else { if ( data instanceof GeomNioFloatData ) bindVertexArray( canvasPeer, data, elemSize, stride, offset, index, arrayType, statesCache, glVBOsSupported, arbVBOsSupported ); } return ( useVBO.booleanValue() ); } private static final int setupBuffers( CanvasPeer canvasPeer, OpenGLStatesCache statesCache, OpenGLCapabilities glCaps, Geometry geoArray, Optimization optimization, int texturesUseMap, boolean glVBOsSupported, boolean arbVBOsSupported ) { final VBOMode geomVBOMode = getGeomVBOMode( optimization ); final Boolean useVBO; final int stride; if ( geoArray.isInterleaved() ) { useVBO = vboModeToBool( geomVBOMode, geoArray.getInterleavedData() ) && ( glVBOsSupported || arbVBOsSupported ); stride = geoArray.getInterleavedData().getStride(); if ( useVBO ) createAndBindVBO( canvasPeer, geoArray.getInterleavedData(), -1, statesCache, glVBOsSupported, arbVBOsSupported ); } else { useVBO = null; // undefined at this point stride = 0; } int vboMap = 0; // Get the normal data. // If the data is cached, but dirty then then disable cache. if ( geoArray.hasNormals() ) { if ( bindGeometryComponent( canvasPeer, statesCache, geoArray.getNormalsData(), geoArray.getNormalsSize(), stride, geoArray.getNormalsOffset(), -1, GL11.GL_NORMAL_ARRAY, geomVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_NORMALS; } // Get the color data. // If the coordinate data is cached, but dirty then then disable cache. if ( geoArray.hasColors() ) { if ( bindGeometryComponent( canvasPeer, statesCache, geoArray.getColorData(), geoArray.getColorsSize(), stride, geoArray.getColorsOffset(), -1, GL11.GL_COLOR_ARRAY, geomVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_COLORS; } if ( texturesUseMap != 0 ) { final VBOMode texCoordVBOMode = geoArray.isInterleaved() ? geomVBOMode : getTexCoordVBOMode( optimization ); final int maxTUs = glCaps.getMaxTextureUnits(); int maskValue = 1; for ( int unit = 0; unit < maxTUs; unit++ ) { if ( ( texturesUseMap & maskValue ) != 0 ) { selectClientTextureUnit( unit, statesCache, false ); final GeomNioFloatData texCoords = geoArray.getTexCoordsData( unit ); if ( geoArray.isInterleaved() ) { final int texCoordSize = geoArray.getTexCoordSize( unit ); final long offset = geoArray.getTexCoordsOffset( unit ); if ( bindGeometryComponent( canvasPeer, statesCache, texCoords, texCoordSize, stride, offset, unit, GL11.GL_TEXTURE_COORD_ARRAY, texCoordVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_TEXTURE_COORDS; } else { if ( bindGeometryComponent( canvasPeer, statesCache, texCoords, texCoords.getElemSize(), 0, 0L, unit, GL11.GL_TEXTURE_COORD_ARRAY, texCoordVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_TEXTURE_COORDS; } } maskValue *= 2; } } // Get the vertex attributes. if ( OpenGlExtensions.GL_CUSTOM_VERTEX_ATTRIBUTES && geoArray.hasVertexAttributes() ) { final int n = Math.min( geoArray.getVertexAttributesCount(), glCaps.getMaxVertexAttributes() ); for ( int i = 0; i < n; i++ ) { if ( geoArray.hasVertexAttributes( i ) ) { final GeomNioFloatData attribData = geoArray.getVertexAttribData( i ); if ( geoArray.isInterleaved() ) { final int attribSize = geoArray.getVertexAttribSize( i ); final long offset = geoArray.getVertexAttribsOffset( i ); if ( bindGeometryComponent( canvasPeer, statesCache, attribData, attribSize, stride, offset, i, GL20.GL_VERTEX_ATTRIB_ARRAY_POINTER, geomVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_VERTEX_ATTRIBS; } else { if ( bindGeometryComponent( canvasPeer, statesCache, attribData, attribData.getElemSize(), 0, 0L, i, GL20.GL_VERTEX_ATTRIB_ARRAY_POINTER, geomVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_VERTEX_ATTRIBS; } } } } // Get the coordinate data. // If the coordinate data is cached, but dirty then then disable cache. if ( bindGeometryComponent( canvasPeer, statesCache, geoArray.getCoordinatesData(), geoArray.getCoordinatesSize(), stride, geoArray.getCoordinatesOffset(), -1, GL11.GL_VERTEX_ARRAY, geomVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_COORDINATES; // Get the coordinate data. // If the coordinate data is cached, but dirty then then disable cache. if ( geoArray.hasIndex() && !geoArray.isInterleaved() ) { if ( bindGeometryComponent( canvasPeer, statesCache, ((IndexedGeometryArray)geoArray).getIndexData(), 1, 0, 0L, -1, 0, geomVBOMode, useVBO, glVBOsSupported, arbVBOsSupported ) ) vboMap |= CARE_MAP_INDICES; } return ( vboMap ); } /** * Draws the geometry. At this point the colors and textures and texture * coordinates should be set prior to rendering. * * @param geoArray * @param useIndexVBO * @param isInDisplayList * * @return the number of rendered triangles */ protected static final int drawBuffers( Geometry geoArray, boolean useIndexVBO, boolean isInDisplayList, boolean isMinVersion13 ) { ProfileTimer.startProfile( X3DLog.LOG_CHANNEL, "ShapeAtomPeer::drawGeometry()" ); final int numVertices = geoArray.getValidVertexCount(); if ( numVertices == 0 ) { X3DLog.exception( "Skipping because there are no valid vertices " ); ProfileTimer.endProfile(); return ( 0 ); } final int mode = geoArray.getType().toOpenGL(); if ( mode == -1 ) { X3DLog.exception( "Skipping unknown geometry : ", geoArray.getClass().getName() ); ProfileTimer.endProfile(); return ( 0 ); // don't know how to draw this } final int triangles; // do the actual drawing genericly if ( geoArray.isStrip() ) { if ( geoArray.hasIndex() ) { final IndexedGeometryStripArray igsa = (IndexedGeometryStripArray)geoArray; final int[] stripVertexCounts = igsa.getStripVertexCounts(); if ( isInDisplayList ) { DisplayListRenderPeer.drawIndexedGeometryStripArray( igsa, mode, igsa.getNumStrips(), stripVertexCounts, isMinVersion13 ); } else { final IntBuffer indexBuffer = igsa.getIndexData().getBuffer(); indexBuffer.rewind(); final int startIndex = igsa.getInitialIndexIndex(); final int numIndices = igsa.getValidIndexCount(); int i0 = 0; for ( int s = 0; s < stripVertexCounts.length; s++ ) { if ( useIndexVBO ) { final int start = ( i0 >= startIndex ) ? i0 : startIndex; int end = start + stripVertexCounts[ s ] - 1; if ( end + 1 > numIndices ) end = numIndices - 1; if ( end < start ) break; GL12.glDrawRangeElements( mode, start, end, end - start + 1, GL11.GL_UNSIGNED_INT, 0L ); } else { IntBuffer buffer = igsa.getIndexData().getBuffer(); int position = buffer.position(); int limit = buffer.limit(); buffer.position( i0 ); buffer.limit( i0 + stripVertexCounts[ s ] ); GL11.glDrawElements( mode, buffer ); buffer.limit( limit ); buffer.position( position ); } i0 += stripVertexCounts[ s ]; } } triangles = igsa.getValidIndexCount() / geoArray.getFaceSize(); } else { final GeometryStripArray geoStripArr = (GeometryStripArray)geoArray; final int stripCount = geoStripArr.getNumStrips(); final int[] stripVertexCounts = geoStripArr.getStripVertexCounts(); if ( isInDisplayList ) { DisplayListRenderPeer.drawGeometryStripArray( geoStripArr, mode, stripCount, stripVertexCounts, isMinVersion13 ); } else { final int startIndex = geoStripArr.getInitialVertexIndex(); final int numIndices = geoStripArr.getValidVertexCount(); // loop through the strips int i0 = 0; for ( int strip = 0; strip < stripCount; strip++ ) { final int start = ( i0 >= startIndex ) ? i0 : startIndex; int end = start + stripVertexCounts[ strip ] - 1; if ( end + 1 > numIndices ) end = numIndices - 1; if ( end < start ) break; GL11.glDrawArrays( mode, start, end - start + 1 ); i0 += stripVertexCounts[ strip ]; } } triangles = geoArray.getValidVertexCount() / geoArray.getFaceSize(); } } else if ( geoArray.hasIndex() ) { final IndexedGeometryArray igeoArray = (IndexedGeometryArray)geoArray; if ( isInDisplayList ) { DisplayListRenderPeer.drawIndexedGeometryArray( igeoArray, mode, isMinVersion13 ); } else { final IntBuffer buffer = igeoArray.getIndexData().getBuffer(); final int startIndex = igeoArray.getInitialIndexIndex(); final int numIndices = igeoArray.getValidIndexCount(); if ( useIndexVBO ) GL12.glDrawRangeElements( mode, startIndex, startIndex + numIndices - 1, numIndices, GL11.GL_UNSIGNED_INT, 0L ); else { // safe the old value int limit = buffer.limit(); buffer.limit( numIndices ); GL11.glDrawElements( mode, buffer ); buffer.limit( limit ); } } triangles = igeoArray.getValidIndexCount() / geoArray.getFaceSize(); } else { if ( isInDisplayList ) { DisplayListRenderPeer.drawRegularGeometryArray( geoArray, mode, isMinVersion13 ); } else { GL11.glDrawArrays( mode, geoArray.getInitialVertexIndex(), numVertices ); } triangles = geoArray.getValidVertexCount() / geoArray.getFaceSize(); } ProfileTimer.endProfile(); return ( triangles ); } public static final void selectClientTextureUnit( int unit, OpenGLStatesCache statesCache, boolean force ) { if ( ( statesCache.enabled && statesCache.currentClientTextureUnit == unit ) && !force ) return; X3DLog.debug( "Activating (client) texture unit ", unit ); final int glUnit = GL13.GL_TEXTURE0 + unit; GL13.glClientActiveTexture( glUnit ); statesCache.currentClientTextureUnit = unit; } /** * Sets the states, if the desired ones differ from the cached ones. * * @param geoArray * @param glCaps * @param statesCache * @param useTextures * @param careMap * * @return a a bist-maks indicating, which texture units are being used */ protected static final int setStates( Geometry geoArray, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, boolean useTextures, int careMap ) { if ( ( careMap & CARE_MAP_COORDINATES ) != 0 ) { if ( !statesCache.enabled || !statesCache.coordsArrayEnabled ) { GL11.glEnableClientState( GL11.GL_VERTEX_ARRAY ); statesCache.coordsArrayEnabled = true; } } if ( ( careMap & CARE_MAP_NORMALS ) != 0 ) { if ( geoArray.hasNormals() ) { if ( !statesCache.enabled || !statesCache.normalsArrayEnabled ) { GL11.glEnableClientState( GL11.GL_NORMAL_ARRAY ); statesCache.normalsArrayEnabled = true; } } else if ( !statesCache.enabled || statesCache.normalsArrayEnabled ) { GL11.glDisableClientState( GL11.GL_NORMAL_ARRAY ); statesCache.normalsArrayEnabled = false; } } if ( ( careMap & CARE_MAP_COLORS ) != 0 ) { if ( geoArray.hasColors() ) { if ( !statesCache.enabled || !statesCache.colorsArrayEnabled ) { GL11.glEnableClientState( GL11.GL_COLOR_ARRAY ); statesCache.colorsArrayEnabled = true; } } else if ( !statesCache.enabled || statesCache.colorsArrayEnabled ) { GL11.glDisableClientState( GL11.GL_COLOR_ARRAY ); statesCache.colorsArrayEnabled = false; } } int result_texturesUseMap = 0; if ( ( careMap & CARE_MAP_TEXTURE_COORDS ) != 0 ) { final int maxTUs = glCaps.getMaxTextureUnits(); int maskValue = 1; for ( int i = 0; i < maxTUs; i++ ) { final boolean hasTextureUnit = ( geoArray.getTexCoordSize( i ) > 0 ); if ( useTextures && hasTextureUnit ) { if ( !statesCache.enabled || ( statesCache.texCoordArraysEnableMask & maskValue ) == 0 ) { selectClientTextureUnit( i, statesCache, false ); GL11.glEnableClientState( GL11.GL_TEXTURE_COORD_ARRAY ); statesCache.texCoordArraysEnableMask |= maskValue; } result_texturesUseMap |= maskValue; } else { if ( !statesCache.enabled || ( statesCache.texCoordArraysEnableMask & maskValue ) != 0 ) { selectClientTextureUnit( i, statesCache, false ); GL11.glDisableClientState( GL11.GL_TEXTURE_COORD_ARRAY ); statesCache.texCoordArraysEnableMask &= ~maskValue; } } maskValue *= 2; } } if ( ( ( careMap & CARE_MAP_VERTEX_ATTRIBS ) != 0 ) && OpenGlExtensions.GL_CUSTOM_VERTEX_ATTRIBUTES && ( geoArray.hasVertexAttributes() || ( !statesCache.enabled || statesCache.maxUsedVertexAttrib > 0 ) ) ) { int maskValue = 1; final int maxVertexAttribs = glCaps.getMaxVertexAttributes(); for ( int i = 0; i < maxVertexAttribs; i++ ) { if ( geoArray.hasVertexAttributes( i ) ) { if ( !statesCache.enabled || ( statesCache.vertexAttribsEnableMask & maskValue ) == 0 ) { GL20.glEnableVertexAttribArray( i ); statesCache.vertexAttribsEnableMask |= maskValue; } if ( !statesCache.enabled || i > statesCache.maxUsedVertexAttrib ) statesCache.maxUsedVertexAttrib = i; } else if ( !statesCache.enabled || ( statesCache.vertexAttribsEnableMask & maskValue ) != 0 ) { GL20.glDisableVertexAttribArray( i ); statesCache.vertexAttribsEnableMask &= ~maskValue; } maskValue *= 2; } } return ( result_texturesUseMap ); } protected static final int renderNoDisplayLists( ShapeAtom shapeAtom, int texturesUseMap, Geometry geoArray, Optimization optimization, CanvasPeer canvasPeer, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, boolean glVBOsSupported, boolean arbVBOsSupported ) { final int vboMap = setupBuffers( canvasPeer, statesCache, glCaps, geoArray, optimization, texturesUseMap, glVBOsSupported, arbVBOsSupported ); final boolean vboForIndex = ( ( vboMap & CARE_MAP_INDICES ) != 0 ); shapeAtom.lastComputedPolysCount = drawBuffers( geoArray, vboForIndex, false, glCaps.isMinVersion13() ); return ( shapeAtom.lastComputedPolysCount ); } protected static final int renderWithForcedVertexArrays( ShapeAtom shapeAtom, int texturesUseMap, Geometry geoArray, CanvasPeer canvasPeer, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache ) { //final Geometry geometry = ( (Shape3D)shapeAtom.getNode() ).getGeometry(); //final Appearance app = ( (Shape3D)shapeAtom.getNode() ).getAppearance(); final Optimization optimization = Optimization.NONE; return ( renderNoDisplayLists( shapeAtom, texturesUseMap, geoArray, optimization, canvasPeer, glCaps, statesCache, false, false ) ); } private static final int render( ShapeAtom shapeAtom, Geometry geometry, Geometry geoArray, CanvasPeer canvasPeer, RenderPeer renderPeer, OpenGLCapabilities glCaps, OpenGLStatesCache statesCache, RenderOptions options, boolean isNormalRenderMode ) { final boolean glVBOsSupported = glCaps.isMinVersion15() && glCaps.supportsVBOs() && options.areVBOsEnabled(); final boolean arbVBOsSupported = OpenGlExtensions.ARB_vertex_buffer_object && glCaps.supportsVBOs() && options.areVBOsEnabled(); Optimization optimization = geometry.getOptimization(); if ( optimization == Geometry.Optimization.USE_DISPLAY_LISTS ) { if ( options.areDisplayListsEnabled() ) { bindArrayVBO( 0, statesCache, glVBOsSupported, arbVBOsSupported ); bindIndexVBO( 0, statesCache, glVBOsSupported, arbVBOsSupported ); // completely bypass states management for DisplayLists! DisplayListRenderPeer.renderDisplayList( shapeAtom, geoArray, canvasPeer, glCaps, statesCache, isNormalRenderMode ); return ( shapeAtom.lastComputedPolysCount ); } optimization = Optimization.USE_VBOS; } final int texturesUseMap = setStates( geoArray, glCaps, statesCache, isNormalRenderMode, CARE_MAP_ALL ); return ( renderNoDisplayLists( shapeAtom, texturesUseMap, geoArray, optimization, canvasPeer, glCaps, statesCache, glVBOsSupported, arbVBOsSupported ) ); } /** * {@inheritDoc} */ @Override public final int renderAtom( RenderAtom< ? > atom, Object glObj, RenderPeer renderPeer, OpenGLCapabilities glCaps, View view, RenderOptions options, long nanoTime, long nanoStep, RenderMode renderMode, long frameId ) { final CanvasPeer canvasPeer = renderPeer.getCanvasPeer(); //final int canvasID = renderPeer.getCanvasPeer().getCanvasID(); final OpenGLStatesCache statesCache = renderPeer.getStatesCache(); final ShapeAtom shapeAtom = (ShapeAtom)atom; final Shape3D shape = (Shape3D)shapeAtom.getNode(); final String shapeName = shape.getName(); final boolean hasName = ( ( shapeName != null ) && ( shapeName.length() > 0 ) ); if ( hasName && ProfileTimer.isProfilingEnabled() ) { ProfileTimer.startProfile( X3DLog.LOG_CHANNEL, shapeName ); } final Geometry geometry = shape.getGeometry(); if ( geometry == null ) { X3DLog.exception( "Shape has no geometry, so skipping : ", shape, " : ", shapeName ); return ( 0 ); } final Appearance app = shape.getAppearance(); final boolean isBillboard = ( shape instanceof Billboard ); setMatrix( view, _SG_PrivilegedAccess.getLeafWorldTransform( shape ), isBillboard, false ); if ( shape.getShowBounds() && ( renderMode == RenderMode.NORMAL ) ) BoundsAtomPeer.drawBounds( shape.getBounds(), null, statesCache ); int triangles = 0; if ( ( app != null ) && ( app.getPolygonAttributes() != null ) && ( app.getPolygonAttributes().getFaceCullMode() == FaceCullMode.SWITCH ) ) { tmpIntBuffer.clear(); GL11.glGetInteger( GL11.GL_CULL_FACE_MODE, tmpIntBuffer ); final int polyMode = tmpIntBuffer.get(); final boolean wasPolyCullEnabled = PolygonAttribsStateUnitPeer.setCullMode( statesCache, PolygonAttributes.CULL_BACK, true, renderMode == RenderMode.PICKING ); triangles = render( shapeAtom, geometry, geometry, canvasPeer, renderPeer, glCaps, statesCache, options, renderMode == RenderMode.NORMAL ); PolygonAttribsStateUnitPeer.setCullMode( statesCache, PolygonAttributes.CULL_FRONT, false, renderMode == RenderMode.PICKING ); triangles = render( shapeAtom, geometry, geometry, canvasPeer, renderPeer, glCaps, statesCache, options, renderMode == RenderMode.NORMAL ); PolygonAttribsStateUnitPeer.setCullMode( statesCache, polyMode, wasPolyCullEnabled, renderMode == RenderMode.PICKING ); } else { triangles = render( shapeAtom, geometry, geometry, canvasPeer, renderPeer, glCaps, statesCache, options, renderMode == RenderMode.NORMAL ); } // since vertex colors can change the current color and // destroy the shader's known state we must restore them. if ( ( renderMode == RenderMode.NORMAL ) && geometry.hasColors() ) { GL11.glColor4f( statesCache.color.getRed(), statesCache.color.getGreen(), statesCache.color.getBlue(), 1f - statesCache.color.getAlpha() ); } if ( hasName && ProfileTimer.isProfilingEnabled() ) { ProfileTimer.endProfile(); } return ( triangles ); } }