001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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 * <xsd:schema targetNamespace="http://www.deegree.org/app" xmlns:app="http://www.deegree.org/app" xmlns:ogc="http://www.opengis.net/ogc" xmlns:deegreewfs="http://www.deegree.org/wfs" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" attributeFormDefault="unqualified"> 165 * <xsd:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/feature.xsd"/> 166 * <xsd:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/geometryAggregates.xsd"/> 167 * <xsd:element name="WPVS" type="app:WPVSType" substitutionGroup="gml:_Feature"/> 168 * <xsd:complexType name="WPVSType"> 169 * <xsd:complexContent> 170 * <xsd:extension base="gml:AbstractFeatureType"> 171 * <xsd:sequence> 172 * <xsd:element name="fk_feature" type="xsd:double"/> 173 * <xsd:element name="geometry" type="gml:GeometryPropertyType"/> 174 * <xsd:element name="shininess" type="xsd:double" minOccurs="0"/> 175 * <xsd:element name="transparency" type="xsd:double" minOccurs="0"/> 176 * <xsd:element name="ambientintensity" type="xsd:double" minOccurs="0"/> 177 * <xsd:element name="specularcolor" type="xsd:string" minOccurs="0"/> 178 * <xsd:element name="diffusecolor" type="xsd:string" minOccurs="0"/> 179 * <xsd:element name="emissivecolor" type="xsd:string" minOccurs="0"/> 180 * <xsd:element name="texturemap" type="xsd:string" minOccurs="0"/> 181 * <xsd:element name="texturecoordinates" type="xsd:string" minOccurs="0"/> 182 * <xsd:element name="texturetype" type="xsd:string" minOccurs="0"/> 183 * <xsd:element name="repeat" type="xsd:integer" minOccurs="0"/> 184 * </xsd:sequence> 185 * </xsd:extension> 186 * </xsd:complexContent> 187 * </xsd:complexType> 188 * </xsd:schema> 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 }