    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/ogcwebservices/wpvs/j3d/Object3DFactory.java $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005     Department of Geography, University of Bonn
    006     and
    007     lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035     ----------------------------------------------------------------------------*/
    036    package org.deegree.ogcwebservices.wpvs.j3d;
    037
    038    import java.awt.image.BufferedImage;
    039    import java.io.IOException;
    040    import java.io.InputStream;
    041    import java.math.BigDecimal;
    042    import java.net.MalformedURLException;
    043    import java.net.URL;
    044    import java.util.ArrayList;
    045    import java.util.HashMap;
    046    import java.util.List;
    047    import java.util.Map;
    048    import java.util.Properties;
    049
    050    import javax.media.j3d.Material;
    051    import javax.vecmath.Color3f;
    052    import javax.vecmath.TexCoord2f;
    053    import javax.xml.namespace.QName;
    054
    055    import org.deegree.datatypes.QualifiedName;
    056    import org.deegree.framework.log.ILogger;
    057    import org.deegree.framework.log.LoggerFactory;
    058    import org.deegree.framework.util.BootLogger;
    059    import org.deegree.framework.util.ImageUtils;
    060    import org.deegree.framework.util.StringTools;
    061    import org.deegree.model.feature.Feature;
    062    import org.deegree.model.feature.FeatureProperty;
    063    import org.deegree.model.spatialschema.Geometry;
    064    import org.deegree.ogcbase.CommonNamespaces;
    065    import org.deegree.ogcwebservices.wpvs.configuration.RenderingConfiguration;
    066
    067    /**
    068     *
    069     *
    070     *
    071     * @version $Revision: 20594 $
    072     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    073     * @author last edited by: $Author: rbezema $
    074     *
    075     *         $Revision: 20594 $, $Date: 2009-11-05 13:26:07 +0100 (Do, 05. Nov 2009) $
    076     *
    077     */
    078    public class Object3DFactory {
    079
    080        private static InputStream materialURL;
    081
    082        private static Properties material = new Properties();
    083
    084        private static final ILogger LOG = LoggerFactory.getLogger( Object3DFactory.class );
    085
    086        /**
    087         * all texture images will be stored on a Map to avoid double loading and creating a BufferedImage from an image
    088         * source (textureMap property)
    089         */
    090        private static Map<String, BufferedImage> textImgMap = new HashMap<String, BufferedImage>( 200 );
    091
    092        private static final QualifiedName objectID = new QualifiedName( new QName( "http://www.deegree.org/app",
    093                                                                                    "fk_feature", "app" ) );
    094
    095        private static final QualifiedName textMapQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    096                                                                                     "texturemap", "app" ) );
    097
    098        private static final QualifiedName city_textMapQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    099                                                                               "textureMap", CommonNamespaces.CITYGMLNS );
    100
    101        private static final QualifiedName textCoordsQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    102                                                                                        "texturecoordinates", "app" ) );
    103
    104        private static final QualifiedName city_textCoordsQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    105                                                                                  "textureCoordinates",
    106                                                                                  CommonNamespaces.CITYGMLNS );
    107
    108        private static final QualifiedName shininessQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    109                                                                                       "shininess", "app" ) );
    110
    111        private static final QualifiedName city_shininessQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    112                                                                                 "shininess", CommonNamespaces.CITYGMLNS );
    113
    114        private static final QualifiedName transparencyQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    115                                                                                          "transparency", "app" ) );
    116
    117        private static final QualifiedName city_transparencyQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    118                                                                                    "transparency",
    119                                                                                    CommonNamespaces.CITYGMLNS );
    120
    121        private static final QualifiedName ambientintensityQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    122                                                                                              "ambientintensity", "app" ) );
    123
    124        private static final QualifiedName city_ambientintensityQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    125                                                                                        "ambientIntensity",
    126                                                                                        CommonNamespaces.CITYGMLNS );
    127
    128        private static final QualifiedName specularcolorQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    129                                                                                           "specularcolor", "app" ) );
    130
    131        private static final QualifiedName city_specularcolorQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    132                                                                                     "specularColor",
    133                                                                                     CommonNamespaces.CITYGMLNS );
    134
    135        private static final QualifiedName diffusecolorQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    136                                                                                          "diffusecolor", "app" ) );
    137
    138        private static final QualifiedName city_diffusecolorQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    139                                                                                    "diffuseColor",
    140                                                                                    CommonNamespaces.CITYGMLNS );
    141
    142        private static final QualifiedName emissivecolorQn = new QualifiedName( new QName( "http://www.deegree.org/app",
    143                                                                                           "emissivecolor", "app" ) );
    144
    145        private static final QualifiedName city_emissivecolorQn = new QualifiedName( CommonNamespaces.CITYGML_PREFIX,
    146                                                                                     "emissiveColor",
    147                                                                                     CommonNamespaces.CITYGMLNS );
    148
    149        private static final RenderingConfiguration rc = RenderingConfiguration.getInstance();
    150        static {
    151            try {
    152                materialURL = Object3DFactory.class.getResourceAsStream( "material.properties" );
    153                material.load( materialURL );
    154            } catch ( IOException e ) {
    155                BootLogger.logError( e.getMessage(), e );
    156            }
    157        }
    158
    159        /**
    160         * creates a Surface from the passed feature. It is assumed the feature is simple, contains one surfac/polygon
    161         * geometry and optional has material and/or texture informations. The GML schema for a valid feature is defined as:
    162         *
    163         * <pre>
    164         *    &lt;xsd:schema targetNamespace=&quot;http://www.deegree.org/app&quot; xmlns:app=&quot;http://www.deegree.org/app&quot; xmlns:ogc=&quot;http://www.opengis.net/ogc&quot; xmlns:deegreewfs=&quot;http://www.deegree.org/wfs&quot; xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot; xmlns:gml=&quot;http://www.opengis.net/gml&quot; elementFormDefault=&quot;qualified&quot; attributeFormDefault=&quot;unqualified&quot;&gt;
    165         *        &lt;xsd:import namespace=&quot;http://www.opengis.net/gml&quot; schemaLocation=&quot;http://schemas.opengis.net/gml/3.1.1/base/feature.xsd&quot;/&gt;
    166         *         &lt;xsd:import namespace=&quot;http://www.opengis.net/gml&quot; schemaLocation=&quot;http://schemas.opengis.net/gml/3.1.1/base/geometryAggregates.xsd&quot;/&gt;
    167         *         &lt;xsd:element name=&quot;WPVS&quot; type=&quot;app:WPVSType&quot; substitutionGroup=&quot;gml:_Feature&quot;/&gt;
    168         *         &lt;xsd:complexType name=&quot;WPVSType&quot;&gt;
    169         *             &lt;xsd:complexContent&gt;
    170         *                 &lt;xsd:extension base=&quot;gml:AbstractFeatureType&quot;&gt;
    171         *                     &lt;xsd:sequence&gt;
    172         *                         &lt;xsd:element name=&quot;fk_feature&quot; type=&quot;xsd:double&quot;/&gt;
    173         *                         &lt;xsd:element name=&quot;geometry&quot; type=&quot;gml:GeometryPropertyType&quot;/&gt;
    174         *                         &lt;xsd:element name=&quot;shininess&quot; type=&quot;xsd:double&quot; minOccurs=&quot;0&quot;/&gt;
    175         *                         &lt;xsd:element name=&quot;transparency&quot; type=&quot;xsd:double&quot; minOccurs=&quot;0&quot;/&gt;
    176         *                         &lt;xsd:element name=&quot;ambientintensity&quot; type=&quot;xsd:double&quot; minOccurs=&quot;0&quot;/&gt;
    177         *                         &lt;xsd:element name=&quot;specularcolor&quot; type=&quot;xsd:string&quot; minOccurs=&quot;0&quot;/&gt;
    178         *                         &lt;xsd:element name=&quot;diffusecolor&quot; type=&quot;xsd:string&quot; minOccurs=&quot;0&quot;/&gt;
    179         *                         &lt;xsd:element name=&quot;emissivecolor&quot; type=&quot;xsd:string&quot; minOccurs=&quot;0&quot;/&gt;
    180         *                         &lt;xsd:element name=&quot;texturemap&quot; type=&quot;xsd:string&quot; minOccurs=&quot;0&quot;/&gt;
    181         *                         &lt;xsd:element name=&quot;texturecoordinates&quot; type=&quot;xsd:string&quot; minOccurs=&quot;0&quot;/&gt;
    182         *                         &lt;xsd:element name=&quot;texturetype&quot; type=&quot;xsd:string&quot; minOccurs=&quot;0&quot;/&gt;
    183         *                         &lt;xsd:element name=&quot;repeat&quot; type=&quot;xsd:integer&quot; minOccurs=&quot;0&quot;/&gt;
    184         *                     &lt;/xsd:sequence&gt;
    185         *                 &lt;/xsd:extension&gt;
    186         *             &lt;/xsd:complexContent&gt;
    187         *         &lt;/xsd:complexType&gt;
    188         *     &lt;/xsd:schema&gt;
    189         * </pre>
    190         *
    191         * @param feature
    192         * @param texturedShapes
    193         *            which were loaded already
    194         * @return a DefaultSurface which is a derivative of a Shape3D. NOTE, the surface is not yet 'compiled'.
    195         */
    196        public DefaultSurface createSurface( Feature feature, Map<String, TexturedSurface> texturedShapes ) {
    197            double oId = -1d;
    198            if ( feature.getDefaultProperty( objectID ) != null ) {
    199                if ( feature.getDefaultProperty( objectID ).getValue( new Double( -1 ) ) instanceof BigDecimal ) {
    200                    oId = ( (BigDecimal) feature.getDefaultProperty( objectID ).getValue( new Double( -1 ) ) ).doubleValue();
    201                } else if ( feature.getDefaultProperty( objectID ).getValue( new Double( -1 ) ) instanceof Double ) {
    202                    oId = ( (Double) feature.getDefaultProperty( objectID ).getValue( new Double( -1d ) ) ).doubleValue();
    203                }
    204            } else {
    205                LOG.logDebug( "use genereted gml:id" );
    206                oId = Math.random();
    207            }
    208
    209            // read texture informations (if available) from feature
    210            BufferedImage textImage = null;
    211            TexturedSurface cachedSurface = null;
    212            String textureFile = null;
    213            FeatureProperty[] fp = feature.getProperties( textMapQn );
    214            if ( fp == null || fp.length == 0 ) {
    215                fp = feature.getProperties( city_textMapQn );
    216            }
    217            if ( fp != null && fp.length > 0 ) {
    218                textureFile = (String) fp[0].getValue();
    219                if ( textureFile != null && !"".equals( textureFile.trim() ) ) {
    220                    if ( texturedShapes.containsKey( textureFile ) ) {
    221                        cachedSurface = texturedShapes.get( textureFile );
    222                    } else {
    223                        textImage = textImgMap.get( textureFile );
    224                        if ( textImage == null ) {
    225                            String lt = textureFile.toLowerCase();
    226                            try {
    227                                if ( lt.startsWith( "file:" ) || lt.startsWith( "http:" ) ) {
    228                                    textImage = ImageUtils.loadImage( new URL( textureFile ) );
    229                                } else {
    230                                    textImage = ImageUtils.loadImage( textureFile );
    231                                }
    232                                // textImage = ImageIO.read( new URL( textureFile ) );
    233
    234                            } catch ( MalformedURLException e ) {
    235                                e.printStackTrace();
    236                            } catch ( IOException e ) {
    237                                e.printStackTrace();
    238                            }
    239                            if ( textImage != null ) {
    240                                textImgMap.put( textureFile, textImage );
    241                            } else {
    242                                LOG.logWarning( "Failed to load texture image: " + textureFile );
    243                            }
    244                        }
    245                    }
    246                }
    247            }
    248            // float[][] textureCoords = new float[1][];
    249            List<TexCoord2f> textureCoords = null;
    250            if ( textImage != null || cachedSurface != null ) {
    251                fp = feature.getProperties( textCoordsQn );
    252                if ( fp == null || fp.length == 0 ) {
    253                    fp = feature.getProperties( city_textCoordsQn );
    254                }
    255                if ( fp != null && fp.length > 0 ) {
    256                    String tmp = (String) fp[0].getValue();
    257                    LOG.logDebug( "Texture Coordinates: " + tmp );
    258                    if ( tmp != null ) {
    259                        float[] tc = StringTools.toArrayFloat( tmp, ", " );
    260                        if ( tc != null && tc.length > 0 ) {
    261                            textureCoords = new ArrayList<TexCoord2f>( tc.length );
    262                            for ( int i = 0; i < tc.length; i += 2 ) {
    263                                textureCoords.add( new TexCoord2f( tc[i], tc[i + 1] ) );
    264                            }
    265                        }
    266                    }
    267                }
    268            }
    269
    270            // read color informations from feature. If not available use default values
    271            // from material.properties
    272            Double shininess = new Double( material.getProperty( "shininess" ) );
    273            fp = feature.getProperties( shininessQn );
    274            if ( fp == null || fp.length == 0 ) {
    275                fp = feature.getProperties( city_shininessQn );
    276            }
    277            if ( fp != null && fp.length > 0 ) {
    278                if ( fp[0].getValue() instanceof String ) {
    279                    shininess = Double.parseDouble( (String) fp[0].getValue( Double.toString( shininess ) ) );
    280                } else {
    281                    shininess = (Double) fp[0].getValue( shininess );
    282                }
    283            }
    284
    285            float transparency = new Float( material.getProperty( "transparency" ) );
    286            fp = feature.getProperties( transparencyQn );
    287            if ( fp == null || fp.length == 0 ) {
    288                fp = feature.getProperties( city_transparencyQn );
    289            }
    290            if ( fp != null && fp.length > 0 ) {
    291                if ( fp[0].getValue() instanceof String ) {
    292                    transparency = Float.parseFloat( (String) fp[0].getValue( Float.toString( transparency ) ) );
    293                } else {
    294                    if ( fp[0].getValue() instanceof Double ) {
    295                        transparency = ( (Double) fp[0].getValue( transparency ) ).floatValue();
    296                    } else {
    297                        transparency = (Float) fp[0].getValue( transparency );
    298                    }
    299                }
    300            }
    301
    302            Double ambientintensity = new Double( material.getProperty( "ambientintensity" ) );
    303            fp = feature.getProperties( ambientintensityQn );
    304            if ( fp == null || fp.length == 0 ) {
    305                fp = feature.getProperties( city_ambientintensityQn );
    306            }
    307            if ( fp != null && fp.length > 0 ) {
    308                if ( fp[0].getValue() instanceof String ) {
    309                    ambientintensity = Double.parseDouble( (String) fp[0].getValue( Double.toString( ambientintensity ) ) );
    310                } else {
    311                    ambientintensity = (Double) fp[0].getValue( ambientintensity );
    312                }
    313            }
    314            Color3f ambientcolor = new Color3f( ambientintensity.floatValue(), ambientintensity.floatValue(),
    315                                                ambientintensity.floatValue() );
    316
    317            String tmp = material.getProperty( "specularcolor" );
    318            fp = feature.getProperties( specularcolorQn );
    319            if ( fp == null || fp.length == 0 ) {
    320                fp = feature.getProperties( city_specularcolorQn );
    321            }
    322            if ( fp != null && fp.length > 0 ) {
    323                tmp = (String) fp[0].getValue( tmp );
    324                tmp = createStringToolsSecureString( tmp, material.getProperty( "specularcolor" ) );
    325            }
    326            float[] tmpFl = StringTools.toArrayFloat( tmp.trim(), " " );
    327            Color3f specularcolor = new Color3f( tmpFl[0], tmpFl[1], tmpFl[2] );
    328
    329            tmp = material.getProperty( "diffusecolor" );
    330            fp = feature.getProperties( diffusecolorQn );
    331            if ( fp == null || fp.length == 0 ) {
    332                fp = feature.getProperties( city_diffusecolorQn );
    333            }
    334            if ( fp != null && fp.length > 0 ) {
    335                tmp = (String) fp[0].getValue( tmp );
    336                tmp = createStringToolsSecureString( tmp, material.getProperty( "diffusecolor" ) );
    337            }
    338            tmpFl = StringTools.toArrayFloat( tmp.trim(), " " );
    339            Color3f diffusecolor = new Color3f( tmpFl[0], tmpFl[1], tmpFl[2] );
    340
    341            tmp = material.getProperty( "emissivecolor" );
    342            fp = feature.getProperties( emissivecolorQn );
    343            if ( fp == null || fp.length == 0 ) {
    344                fp = feature.getProperties( city_emissivecolorQn );
    345            }
    346            if ( fp != null && fp.length > 0 ) {
    347                tmp = (String) fp[0].getValue( tmp );
    348                tmp = createStringToolsSecureString( tmp, material.getProperty( "emissivecolor" ) );
    349            }
    350            tmpFl = StringTools.toArrayFloat( tmp.trim(), " " );
    351            Color3f emissivecolor = new Color3f( tmpFl[0], tmpFl[1], tmpFl[2] );
    352
    353            Material mat = new Material( ambientcolor, emissivecolor, diffusecolor, specularcolor, shininess.floatValue() );
    354            // for diffuse-color to work the ambient lighting must be switched on
    355            mat.setColorTarget( Material.AMBIENT_AND_DIFFUSE );
    356
    357            /**
    358             * Please check for the right property here. The defaultPropertyValue just delivers the first geometry defined
    359             * in the wfs configuration.
    360             */
    361            DefaultSurface resultSurface = null;
    362            org.deegree.model.spatialschema.Geometry geometry = feature.getDefaultGeometryPropertyValue();
    363            // if ( geometry instanceof MultiSurface ) {
    364            // MultiSurface multiSurfaces = (MultiSurface) geometry;
    365            // LOG.logDebug( "Found a Multi surface" );
    366            // Surface[] allSurfaces = multiSurfaces.getAllSurfaces();
    367            // // so no texture create a new Default surface
    368            // } else if ( geometry instanceof Surface ) {
    369            resultSurface = createSurface( texturedShapes, cachedSurface, geometry, textImage, textureFile, mat,
    370                                           transparency, feature.getId(), Double.toString( oId ), textureCoords );
    371            if ( resultSurface instanceof TexturedSurface ) {
    372                // necessary for the caching mechanism of textures, because they will be added to the map later.
    373                resultSurface = null;
    374            }
    375            //
    376            // }
    377            // surface.compile();
    378            return resultSurface;
    379        }
    380
    381        private String createStringToolsSecureString( String someColor, String defaultValue ) {
    382            if ( "".equals( someColor.trim() ) ) {
    383                someColor = defaultValue;
    384            } else {
    385                String[] s = someColor.split( "\\s" );
    386                if ( s.length == 1 || s.length > 3 ) {
    387                    someColor = defaultValue;
    388                } else {
    389                    StringBuilder sb = new StringBuilder( 50 );
    390                    for ( int i = 0; i < s.length; i++ ) {
    391                        sb.append( s[i] ).append( " " );
    392                    }
    393                    someColor = sb.toString().trim();
    394                }
    395            }
    396            return someColor;
    397        }
    398
    399        private DefaultSurface createSurface( Map<String, TexturedSurface> texturedShapes, TexturedSurface cachedSurface,
    400                                              Geometry surfaceToAdd, BufferedImage textImage, String textureFile,
    401                                              Material material, float transparency, String id, String parentId,
    402                                              List<TexCoord2f> textureCoords ) {
    403            DefaultSurface result = null;
    404            if ( cachedSurface != null ) {
    405                result = cachedSurface;
    406                LOG.logDebug( "Textured-Surface cached" );
    407                cachedSurface.addGeometry( surfaceToAdd, textureCoords );
    408            } else if ( textImage != null ) {
    409                LOG.logDebug( "Textured-Surface not cached" );
    410                result = new TexturedSurface( id, parentId, surfaceToAdd, material, transparency, textImage, textureCoords );
    411                texturedShapes.put( textureFile, (TexturedSurface) result );
    412            } else {
    413                LOG.logDebug( "3D-Surface without texture: ", surfaceToAdd );
    414                result = new ColoredSurface( id, parentId, surfaceToAdd, material, transparency );
    415            }
    416            return result;
    417        }
    418    }