Package org.geotools.renderer3d.terrainblock

Source Code of org.geotools.renderer3d.terrainblock.TerrainBlockImpl

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2007-2008, Open Source Geospatial Foundation (OSGeo)
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*    This library 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
*    Lesser General Public License for more details.
*/
package org.geotools.renderer3d.terrainblock;

import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import org.geotools.renderer3d.provider.texture.impl.TextureListener;
import org.geotools.renderer3d.provider.texture.impl.TextureProvider;
import org.geotools.renderer3d.utils.BoundingRectangle;
import org.geotools.renderer3d.utils.BoundingRectangleImpl;
import org.geotools.renderer3d.utils.ParameterChecker;
import org.geotools.renderer3d.utils.quadtree.NodeListener;
import org.geotools.renderer3d.utils.quadtree.QuadTreeNode;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

/**
* TODO: Store the texture image related to the terrain block in this class, create it here (based on parent block one),
* apply it to the mesh, and pass a reference of it to the texture calculation method.  That way texture memory management
* will be simplified.
*
* @author Hans H�ggstr�m
*/
public final class TerrainBlockImpl
        implements TerrainBlock, NodeListener<TerrainBlock>
{

    //======================================================================
    // Private Fields

    private final int myNumberOfGridsPerSide;
    private final BufferedImage myTextureImage;

    private final TextureProvider myTextureProvider;
    private final TextureListener myTextureListener = new TextureListener()
    {

        public void onTextureReady( final BoundingRectangle area, final BufferedImage texture )
        {
            // The texture has been calculated to the image instance we hold in this terrain block,
            // so just apply it to the terrain mesh if it has been created already.
            if ( myTerrainMesh != null )
            {
                myTerrainMesh.setTextureImage( myTextureImage );
            }

            myHasCalculatedTextureImage = true;
        }

    };
    private final int myTextureSize;

    private Vector3f myCenter;

    private QuadTreeNode<TerrainBlock> myQuadTreeNode;

    private List<Spatial> myChildNodeSpatials = new ArrayList<Spatial>( 4 );

    private TerrainMesh myTerrainMesh = null;
    private Node myTerrain3DNode = null;

    private boolean myHasCalculatedTextureImage = false;
    private Texture myTempParentTextureToUse = null;

    //======================================================================
    // Private Constants

    private static final BoundingRectangleImpl WHOLE_TEXTURE = new BoundingRectangleImpl( 0, 0, 1, 1 );
    private static final BoundingRectangleImpl WHOLE_TEXTURE_FLIPPED_ALONG_Y = new BoundingRectangleImpl( 0, 1, 1, 0 );

    //======================================================================
    // Public Methods

    //----------------------------------------------------------------------
    // Constructors

    /**
     * @param quadTreeNode
     * @param numberOfGridsPerSide number of grid cells along the side of the TerrainBlock.
     * @param textureSize
     * @param textureProvider
     */
    public TerrainBlockImpl( final QuadTreeNode<TerrainBlock> quadTreeNode,
                             final int numberOfGridsPerSide,
                             final int textureSize,
                             final TextureProvider textureProvider )
    {
        ParameterChecker.checkNotNull( quadTreeNode, "quadTreeNode" );
        ParameterChecker.checkPositiveNonZeroInteger( numberOfGridsPerSide, "numberOfGridsPerSide" );
        ParameterChecker.checkPositiveNonZeroInteger( textureSize, "textureSize" );
        ParameterChecker.checkNotNull( textureProvider, "textureProvider" );

        myQuadTreeNode = quadTreeNode;
        myNumberOfGridsPerSide = numberOfGridsPerSide;
        myTextureProvider = textureProvider;
        myTextureSize = textureSize;

        myTextureImage = allocateTextureImage( textureSize );

        updateDerivedData();

        myQuadTreeNode.addNodeListener( this );
    }

    //----------------------------------------------------------------------
    // NodeListener Implementation

    public void onCollapsed( QuadTreeNode<TerrainBlock> quadTreeNode )
    {
        if ( myTerrain3DNode != null )
        {
            for ( Spatial childNodeSpatial : myChildNodeSpatials )
            {
                myTerrain3DNode.detachChild( childNodeSpatial );
            }
            myChildNodeSpatials.clear();

            myTerrain3DNode.attachChild( getOrCreateTerrainMesh() );
        }
    }


    public void onExpanded( QuadTreeNode<TerrainBlock> quadTreeNode )
    {
        if ( myTerrain3DNode != null )
        {
            if ( myTerrainMesh != null )
            {
                myTerrain3DNode.detachChild( myTerrainMesh );
            }

            attachChildNodeSpatials();
        }
    }

    //----------------------------------------------------------------------
    // TerrainBlock Implementation

    public Spatial getSpatial()
    {
        if ( myTerrain3DNode == null )
        {
            myTerrain3DNode = new Node();

            if ( myQuadTreeNode.isExpanded() )
            {
                attachChildNodeSpatials();
            }
            else
            {
                myTerrain3DNode.attachChild( getOrCreateTerrainMesh() );
            }
        }


        return myTerrain3DNode;
    }


    public Vector3f getCenter()
    {
        return myCenter;
    }


    public void updateDerivedData()
    {
        myHasCalculatedTextureImage = false;

        // Remove previous texture request, if found
        myTextureProvider.cancelRequest( myTextureListener );

        // Update center
        final BoundingRectangle bounds = myQuadTreeNode.getBounds();
        myCenter = new Vector3f( (float) bounds.getCenterX(),
                                 (float) bounds.getCenterY(),
                                 0 ); // TODO: Get ground height at center.

        // Update terrain mesh if present
        if ( myTerrainMesh != null )
        {
            ///fillTextureImageWithLoadingGraphics();
            myTerrainMesh.updateBounds( bounds.getX1(), bounds.getY1(), bounds.getX2(), bounds.getY2() );
//            myTerrainMesh.setTextureImage( myTextureImage );

            // Copy a chunk of a previously calculated parent block texture to the texture of this block
            initializeTextureFromParentTexture();
        }

        // Make sure the node is collapsed
        onCollapsed( myQuadTreeNode );

        // Request texture for terrain block
        myTextureProvider.requestTexture( bounds, myTextureImage, myTextureListener );
    }


    public BufferedImage getTextureImage()
    {
        return myTextureImage;
    }


    public boolean hasCalculatedTextureImage()
    {
        return myTerrainMesh != null &&
               !myTerrainMesh.isPlaceholderTextureInUse() &&
               myHasCalculatedTextureImage;
    }


    public void clearPicture()
    {
        fillTextureImageWithLoadingGraphics();
    }


    public Texture getTexture()
    {
        if ( myTerrainMesh != null )
        {
            return myTerrainMesh.getTexture();
        }
        else
        {
            return null;
        }
    }

    //======================================================================
    // Private Methods

    private void initializeTextureFromParentTexture()
    {
        myTempParentTextureToUse = null;
        final BoundingRectangle area = calculatePlaceholderTextureAndArea( myQuadTreeNode );

        setPlaceholderTexture( myTempParentTextureToUse, area );
    }


    /**
     * Recursive function to calculate the area and texture to use.
     *
     * @return the area to use of the myTempParentTextureToUse, or null if no parent texture is available to use.
     */
    private BoundingRectangle calculatePlaceholderTextureAndArea( final QuadTreeNode<TerrainBlock> node )
    {
        if ( node == null )
        {
            // No texture even in root
            myTempParentTextureToUse = null;
            return null;
        }
        else
        {
            final TerrainBlock block = node.getNodeData();
            final Texture texture = block.getTexture();
            if ( block.hasCalculatedTextureImage() && texture != null )
            {
                // Found texture in current node, use it.
                myTempParentTextureToUse = texture;
                return WHOLE_TEXTURE;
            }
            else
            {
                // Get area and texture from parent
                final QuadTreeNode<TerrainBlock> parentNode = node.getParent();
                final BoundingRectangle area = calculatePlaceholderTextureAndArea( parentNode );

                if ( area == null )
                {
                    return null;
                }
                else
                {
                    // Calculate location of this node in the parent area
                    int quadrant = parentNode.getIndexOfChild( node );

                    // Compensate for some texture flipping complications
                    quadrant = area.flipSubquadrantAcrossY( quadrant );

                    return area.createSubquadrantBoundingRectangle( quadrant );
                }
            }
        }
    }


    private void setPlaceholderTexture( final Texture texture, final BoundingRectangle textureArea )
    {
        if ( myTerrainMesh != null )
        {
            myTerrainMesh.setPlaceholderTexture( texture, textureArea );
        }
    }


    private void fillTextureImageWithLoadingGraphics()
    {
        final Graphics graphics = myTextureImage.getGraphics();

        // Background color
        graphics.setColor( Color.GRAY );
        graphics.fillRect( 0, 0, myTextureImage.getWidth(), myTextureImage.getHeight() );

        // Text
        graphics.setColor( Color.BLACK );
        graphics.drawString( "Rendering Texture", 0, myTextureImage.getHeight() / 2 );
    }


    private BufferedImage allocateTextureImage( final int textureSize )
    {
        // Try to allocate the texture space, if we are out of memory just use null (a placeholder texture will be used)
        BufferedImage mapImage;
        try
        {
            mapImage = new BufferedImage( textureSize, textureSize, BufferedImage.TYPE_4BYTE_ABGR );
        }
        catch ( OutOfMemoryError e )
        {
            mapImage = null;
        }

        return mapImage;
    }


    private void attachChildNodeSpatials()
    {
        myChildNodeSpatials.clear();
        for ( int i = 0; i < myQuadTreeNode.getNumberOfChildren(); i++ )
        {
            final QuadTreeNode<TerrainBlock> child = myQuadTreeNode.getChild( i );
            if ( child != null )
            {
                final TerrainBlock childBlock = child.getNodeData();
                if ( childBlock != null )
                {
                    final Spatial childSpatial = childBlock.getSpatial();
                    myChildNodeSpatials.add( childSpatial );
                    myTerrain3DNode.attachChild( childSpatial );
                }
            }
        }
    }


    private TerrainMesh getOrCreateTerrainMesh()
    {
        if ( myTerrainMesh == null )
        {
            myTerrainMesh = createTerrainMesh();
        }

        return myTerrainMesh;
    }


    private TerrainMesh createTerrainMesh()
    {
        final float z = 0;
        final TerrainMesh terrainMesh = new TerrainMesh( myNumberOfGridsPerSide,
                                                         myNumberOfGridsPerSide,
                                                         myQuadTreeNode.getBounds().getX1(),
                                                         myQuadTreeNode.getBounds().getY1(),
                                                         myQuadTreeNode.getBounds().getX2(),
                                                         myQuadTreeNode.getBounds().getY2(),
                                                         z );

        fillTextureImageWithLoadingGraphics();
        terrainMesh.setTextureImage( myTextureImage );

        // Copy a chunk of a previously calculated parent block texture to the texture of this block
        initializeTextureFromParentTexture();

        return terrainMesh;
    }

}
TOP

Related Classes of org.geotools.renderer3d.terrainblock.TerrainBlockImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.