Hi,

I've found the following two bugs in J3D VRML classes
(downloaded from x3d's CVS repository):

1) IndexedLineSet is not colored if the color attribute is not
   specified but the appearance is. VRML97 specification says:

   If the color field is NULL and there is a Material defined for
   the Appearance affecting this IndexedLineSet, the emissiveColor
   of the Material shall be used to draw the lines.

   CosmoPlayer renders this correctly. See the attached 3dx.wrl.

2) very poor performance
   If I use the previous released version (I think 0.90.2), one of
   my scenes is loaded in 13 seconds. Parsing of the same scene
   with the  last version took about 260 seconds!!!


System: Win98, JDK 1.2.2, J3d 1.1.3, 500 MHz PIII, 128 Mb RAM.


Solution(s)

1) The following code in Shape.java, method initImpl() was
   added (marked with >):

                if ((geomNode instanceof IndexedLineSet) ||
                    (geomNode instanceof PointSet)) {
                    javax.media.j3d.Material material =
                                appNode.impl.getMaterial();
                    if (material == null) {
                        material = new javax.media.j3d.Material();
                        appNode.impl.setMaterial(material);
                    }
>                     else {
>                        ColoringAttributes ca = new ColoringAttributes();
>                        Color3f color = new Color3f();
>                        material.getEmissiveColor(color);
>                        ca.setColor(color);
>                        appNode.impl.setColoringAttributes(ca);
>                    }

                    material.setLightingEnable(false);

   The complete and working file Shape.java is attached.


2) I don't know the correct solution, but the reason lies in the
   class Time, method getNow(). The following loop spends time for
   nothing:

        // Make sure we don't ever return the same time stamp for two
        // separate calls
        while (curTimeMillis <= lastTimeMillis) {
            curTimeMillis=System.currentTimeMillis();
        }

   I suppose this is also related to timer resolution on Win platform.
   Finer resolution should speed up scene building, but this is still
   not correct solution. During parsing the loop should be switched
off.
   After I changed the code to:

    public static double getNow() {
        long curTimeMillis = System.currentTimeMillis();

        // Make sure we don't ever return the same timestamp for two
        // separate calls
        /*      while (curTimeMillis <= lastTimeMillis) {
            curTimeMillis=System.currentTimeMillis();
            } */
        lastTimeMillis++;
        curTimeMillis = lastTimeMillis;

        return ((curTimeMillis / 1000.0)-systemInit);

    }

   the scene was loaded in 10 seconds, but now there is no
   real time :-(. (this approach works for me, since my VRML
   worlds are static).


I would like someone, who is familiar with Java3D VRML browser code
to verify and integrate the first solution. An acceptable
solution for the second problem would also be useful for everybody
who is using the browser with dynamic worlds.


Regards,

Marko

3dx.wrl

/*
 *      @(#)Shape.java 1.45 99/03/11 14:58:12
 *
 * Copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

/*
 * @Author: Rick Goldberg
 * @Author: Doug Gehringer
 *
 */

package com.sun.j3d.loaders.vrml97.impl;
import javax.media.j3d.Shape3D;
import javax.media.j3d.ColoringAttributes;
import javax.vecmath.Color3f;
import javax.media.j3d.BoundingBox;

public class Shape extends NonSharedNode {

    //exposedField
    SFNode appearance;
    SFNode geometry;

    int numTris = 0;
    boolean ifsChangeable = false;

    public Shape(Loader loader) {
        super(loader);
        appearance = new SFNode( null );
        geometry = new SFNode( null );

        initFields();
    }

    public Shape(Loader loader, SFNode appearancenode, SFNode geonode ) {
        super(loader);
        appearance = appearancenode;
        geometry = geonode;

        initFields();
    }

    void initFields() {
        appearance.init(this, FieldSpec, Field.EXPOSED_FIELD, "appearance");
        geometry.init(  this, FieldSpec, Field.EXPOSED_FIELD, "geometry");
    }

    public void initImpl() {
        Appearance appNode = (Appearance)appearance.node;
        Geometry geomNode = (Geometry)geometry.node;
        implNode = null;

        if (appNode == null)  {
            appNode = new Appearance(loader);
            appNode.initImpl();
        }
        if( geomNode instanceof Text) {
            Text textNode = (Text)geomNode;
            implNode = textNode.createText2D(appNode.impl);
            implReady = true;
        } else if( geomNode instanceof GroupGeom ) {
            implNode = ((GroupGeom)geomNode).initGroupImpl(appNode.impl);
            implReady = true;
        } else {
            if (geomNode != null) {
                javax.media.j3d.Geometry geom = geomNode.getImplGeom();
                if (geom != null) {
                    if (appearance.node == null) {
                        implNode = new Shape3D(geom);
                    } else {
                        implNode = new Shape3D(geom, appNode.impl);
                    }
                }
            }
            if ((appNode != null) && (geomNode != null)) {
                appNode.numUses++;
                if (appNode.haveTexture && !geomNode.haveTexture()) {
                    // need to to a texGen
                    appNode.setTexGen(geomNode.getBoundingBox());
                }

                if (geomNode instanceof Ownable) {
                    // set the ifs owner to this
                    ((Ownable)geomNode).setOwner(this);
                    if ( ((Ownable)geomNode).getSolid() == false ) {
                        javax.media.j3d.PolygonAttributes pa =
                                appNode.impl.getPolygonAttributes();
                        if (pa == null) {
                            pa = new javax.media.j3d.PolygonAttributes();
                            // set a default pa then
                            appNode.impl.setPolygonAttributes(pa);
                        }
                        appNode.impl.getPolygonAttributes().setCullFace(
                                javax.media.j3d.PolygonAttributes.CULL_NONE);
                        appNode.impl.getPolygonAttributes().
                                setBackFaceNormalFlip(true);
                    }
                }
                if ((geomNode instanceof IndexedLineSet) ||
                    (geomNode instanceof PointSet)) {
                    javax.media.j3d.Material material =
                                appNode.impl.getMaterial();
                    if (material == null) {
                        material = new javax.media.j3d.Material();
                        appNode.impl.setMaterial(material);
                    } else {
                        // The VRML97 specificatin says:
                        //   If the color field is NULL and there is a Material
                        //   defined for the Appearance affecting this
                        //   IndexedLineSet, the emissiveColor of the Material
                        //   shall be used to draw the lines.
                        ColoringAttributes ca = new ColoringAttributes();
                        Color3f color = new Color3f();
                        material.getEmissiveColor(color);
                        ca.setColor(color);
                        appNode.impl.setColoringAttributes(ca);
                    }
                    material.setLightingEnable(false);
                }
            }

            if ((defName == null) && (loader.loaderMode != Loader.LOAD_CONVERTER)) {
                // If not a DEF'd node, null out those references to free up
                // the memory or if we are using the database for the x3d convesion
                if (loader.debug) {
                    System.out.println("Shape.initImpl(): nulling refrences");
                }
                if (loader.curProto == null) {
                    geometry.node = null;
                    appearance.node = null;
                }
            }
            implReady = true;
        }
        if (implNode!=null) {
            //implNode.setCapability(javax.media.j3d.Node.ALLOW_BOUNDS_READ);
            //implNode.setCapability(
            //      javax.media.j3d.Node.ALLOW_LOCAL_TO_VWORLD_READ);
            numTris = geomNode.getNumTris();
            if (ifsChangeable) {
                ((Shape3D)implNode).setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
                ((Shape3D)implNode).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
            }
        }
    }

    public int getNumTris() {
        //if (loader.debug) {
        //    System.out.println("Shape num tris: " + numTris);
        //}
        return numTris;
    }

    public Object clone() {
        if (loader.debug) {
           System.out.println("Shape.clone() called");
        }
        Shape retval = new Shape(loader, (SFNode)appearance.clone(),
                (SFNode)geometry.clone());
        if (loader.debug) {
           System.out.println("Shape.clone() returns " + retval.toStringId() +
                " = " + retval);
        }
        return retval;
    }

    public String getType() { return "Shape" ; }

    public void notifyMethod(String eventInName, double time) {
        if (eventInName.equals("geometry")) {
            if (implReady && (implNode == null)) {
                // TODO: node needs to get attached to parent.
                initImpl();
            } else {
                if ((implNode instanceof Shape3D) &&
                        !(geometry.node instanceof GroupGeom)) {
                    try {
                        //Shape3D s = (Shape3D)implNode;
                        //((Shape3D)implNode).setGeometry(
                            //((Geometry)geometry.node).getImplGeom());
                        initImpl();
                    } catch (NullPointerException npe ) {System.out.println(npe);}
                } else {
                    System.err.println(
                        "Shape: Unimplemented case replacing geometry");
                }
            }
        } else if (eventInName.equals("appearance")) {
            if (implNode != null) {
                Appearance app = (Appearance)appearance.node;
                if (implNode instanceof Shape3D) {
                    ((Shape3D)implNode).setAppearance(app.impl);
                } else {
                    System.err.println(
                        "Shape: Unimplemented case replacing appearance");
                }
            }
        } else if (eventInName.equals("route_ifs_changeable")) {
            ifsChangeable = true;
        }
    }

    public String toStringBody() {
        String retval = "Shape {\n";
        if (appearance.node != null) {
            retval += "appearance " + appearance;
        }
        if (geometry.node != null) {
            retval += "geometry " + geometry;
        }
        retval += "}";
        return retval;
    }

}

Reply via email to