001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }