/** * 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.loaders.models.util.meta; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.jagatoo.loaders.IncorrectFormatException; import org.jagatoo.loaders.ParsingException; import org.openmali.vecmath2.AxisAngle3f; import org.openmali.vecmath2.Vector3f; import org.xith3d.loaders.models.Model; import org.xith3d.loaders.models.ModelLoader; import org.xith3d.scenegraph.Shape3D; import org.xith3d.scenegraph.StaticTransform; import org.xith3d.scenegraph.TransformGroup; import org.xith3d.utility.logging.X3DLog; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXParseException; /** * A ModelLoader wrapper. Enables a model meta data file (in xml) to be * loaded. This file contains information about scaling and rotating * the model. * * Model Meta Data Format: * TODO Create Format Documentation * * Look at demo/models/meta/galleon.xml for an example * * @author Andrew Hanson (aka Patheros) * @author Marvin Froehlich (aka Qudus) [converted to a SAX-Parser based implementation] */ public class MetaLoader< D extends ModelMetaData > extends ModelLoader { /** * Parse-handler for meta data XML. * * @author Marvin Froehlich (aka Qudus) */ private static class MetaParser extends org.xml.sax.helpers.DefaultHandler { private int level = -1; private String[] path = new String[ 16 ]; private ModelMetaData meta = new ModelMetaData(); private final String getCurrentPathAsString() { String p = ""; for ( int i = 0; i < level; i++ ) { p += '/' + path[i]; } return ( p ); } /* @Override public void startDocument() throws SAXException { //System.out.println( "startDocument" ); } */ @Override public void startElement( String uri, String localName, String qName, Attributes attributes ) throws SAXException { //System.out.println( "startElement( " + uri + ", " + localName + ", " + qName + ", " + attributes + " )" ); path[ ++level ] = qName; if ( qName.equals( "resource" ) ) { String type = attributes.getValue( "type" ); if ( type.equals( "base" ) ) { meta.setResourceRefrenceBase(); } else// if ( type.equals( "relative" ) ) { meta.setResourceRefrenceRelative(); } } else if ( qName.equals( "rotation" ) ) { String type = attributes.getValue( "type" ); if ( type.equals( "AXIS_ANGLE" ) ) { float x = Float.parseFloat( attributes.getValue( "x" ) ); float y = Float.parseFloat( attributes.getValue( "y" ) ); float z = Float.parseFloat( attributes.getValue( "z" ) ); float angle = Float.parseFloat( attributes.getValue( "angle" ) ); meta.setRotation( new AxisAngle3f( x, y, z, angle ) ); } } else if ( qName.equals( "loadingFlag" ) ) { String name = attributes.getValue( "name" ); boolean value = Boolean.parseBoolean( attributes.getValue( "value" ) ); if ( name.equals( "LIGHT_NODES" ) ) { meta.getLoadingFlags().lightNodes = value; } else if ( name.equals( "FOG_NODES" ) ) { meta.getLoadingFlags().fogNodes = value; } else if ( name.equals( "BACKGROUND_NODES" ) ) { meta.getLoadingFlags().backgroundNodes = value; } else if ( name.equals( "BEHAVIOR_NODES" ) ) { meta.getLoadingFlags().behaviorNodes = value; } else if ( name.equals( "VIEW_GROUPS" ) ) { meta.getLoadingFlags().viewGroups = value; } else if ( name.equals( "SOUND_NODES" ) ) { meta.getLoadingFlags().soundNodes = value; } else if ( name.equals( "USE_DISPLAY_LISTS" ) ) { meta.getLoadingFlags().useDisplayLists = value; } } } @Override public void characters( char[] data, int start, int length ) throws SAXException { //System.out.println( "data: " + new String( data, start, length ) ); if ( path[ level ].equals( "resource" ) ) { meta.setResourceName( new String( data, start, length ).trim() ); } else if ( path[ level ].equals( "scaling" ) ) { meta.setScaling( Float.parseFloat( new String( data, start, length ).trim() ) ); } } @Override public void endElement( String uri, String localName, String qName ) throws SAXException { //System.out.println( "endElement( " + uri + ", " + localName + ", " + qName + " )" ); path[ level-- ] = null; } @Override public void warning( SAXParseException e ) throws SAXException { System.err.println( "Warning at: " + getCurrentPathAsString() ); e.printStackTrace(); } @Override public void error( SAXParseException e ) throws SAXException { System.err.println( "Error at: " + getCurrentPathAsString() ); e.printStackTrace(); } @Override public void fatalError( SAXParseException e ) throws SAXException { System.err.println( "Warning at: " + getCurrentPathAsString() ); //e.printStackTrace(); throw e; } /* @Override public void endDocument() throws SAXException { //System.out.println( "endDocument" ); } */ public ModelMetaData getMetaData() { return ( meta ); } } private static final SAXParserFactory SAX_PARSER_FACTORY = SAXParserFactory.newInstance(); private URL metaBaseURL; /** * Peforms any processing on the model defined by the metaData. * Subclasses can extend this functionality if they have their own * desired actions. * @param model the model to act on * @param meta the metaData defining the actions */ private void process( Model model, D meta ) { model.setMetaData( meta ); //Start Scaling and Rotation float scale = meta.getScaling(); AxisAngle3f rotation = meta.getRotation(); Vector3f rotationAngle; if ( rotation != null ) { rotationAngle = new Vector3f( rotation.getX(), rotation.getY(), rotation.getZ() ); if ( rotationAngle.lengthSquared() == 0 ) { ParsingException e = new ParsingException( "Rotation Axis can not be length 0" ); X3DLog.print( e ); throw e; } } else { rotationAngle = null; } for ( int i = 0; i < model.getShapesCount(); i++ ) { final Shape3D shape = model.getShape( i ); StaticTransform.scale( shape, scale ); if ( rotation != null ) { StaticTransform.rotate( shape, rotationAngle, rotation.getAngle() ); } } final List< TransformGroup > transformGroups = model.findAll( TransformGroup.class ); if ( transformGroups != null ) { for ( int i = 0; i < transformGroups.size(); i++ ) { //final TransformGroup transformGroup = transformGroups.get( i ); //StaticTransform.scale( transformGroup, scale ); if ( rotation != null ) { // TODO: Rotate TransformGroup //GeometricMath.rotate(transformGroup, rotationAngle, rotation.angle); } } } // End Scaling and Rotation } public void setMetaBaseURL( URL metaBaseURL ) { this.metaBaseURL = metaBaseURL; } public URL getMetaBaseURL() { return ( metaBaseURL ); } /** * Creates the URL from a given meta. * Subclasses can overide this if they define * their own resource locations. * * @param baseURL * @param meta * * @throws MalformedURLException */ private URL createMetaURL( URL baseURL, D meta ) throws MalformedURLException { URL retVal = null; if ( meta.isResourceRefrenceBase() ) { retVal = new URL( getMetaBaseURL(), meta.getResourceName() ); } else if ( meta.isResourceRefrenceRelative() ) { retVal = new URL( baseURL, meta.getResourceName() ); } return ( retVal ); } @SuppressWarnings( "unchecked" ) private final D loadMeta( URL url ) throws IOException { SAXParser saxParser; try { saxParser = SAX_PARSER_FACTORY.newSAXParser(); } catch ( ParserConfigurationException e ) { throw new Error ( e ); } catch ( SAXException e ) { throw new Error ( e ); } try { saxParser.setProperty( "http://xml.org/sax/features/validation", false ); } catch ( SAXNotRecognizedException e ) { e.printStackTrace(); } catch ( SAXNotSupportedException e ) { e.printStackTrace(); } MetaParser handler = new MetaParser(); try { saxParser.parse( url.openStream(), handler ); } catch ( SAXException e ) { throw new ParsingException( e ); } return ( (D)handler.getMetaData() ); } /** * {@inheritDoc} */ @Override public Model loadModel( URL url, String filenameBase, URL baseURL, String skin, float scale, int flags ) throws IOException, IncorrectFormatException, ParsingException { D meta = loadMeta( url ); Model model = ModelLoader.getInstance().loadModel( createMetaURL( baseURL, meta ), skin ); process( model, meta ); return ( model ); } /** * Constucts MetaLoader. */ public MetaLoader() { super(); } }