Java Standard: Java3D

Aus Wikibooks


Download[Bearbeiten]

Bevor Sie mit Java3D arbeiten können, müssen Sie dieses herunterladen: http://java3d.java.net/binary-builds.html

Am besten laden Sie sich das ZIP-Archiv herunter und entpacken Sie es in Ihren Classpath.

Grundlegendes[Bearbeiten]

Java3D ist eine Highlevel 3D-Graphik-API und wurde 1997 von SUN, HP und SGI initiiert. Im Gegensatz zu OpenGL und DirectX verwendet Java3D einen Szenengraphen zur Verwaltung der 3D-Objekte und benutzt lowlevel Rendering-APIs, wie OpenGL und DirectX, zum Darstellen der 3D-Objekte. Vom Szenengraphen her ähnelt Java3D stark VRML2, allerdings sind die Events zwischen den Objekten nicht als ROUTES sondern als spezielle Java-Events realisiert. Wie VRML2 ist Java3D nicht nur in der Lage zu 3D-Objekte darzustellen, sondern kann auch Sound, Verhalten, Animationen und unkonventionelle Eingabe- und Ausgabegeräte managen. 3D-Objekte müssen nicht in Java3D Geometrieformat abgelegt sein - es gibt auch eine ganze Anzahl von Loadern für verschiedene 3D-Formate wie OBJ, WRL, 3DS oder DXF.

Java3D wird nicht mit den offiziellen Java SDK- oder JRE-Releases ausgeliefert. Aus diesem Grund muß Java3D manuell ins Java-System eingebunden werden.

Das Programmieren in Java3D ist erst einmal gar nicht so einfach, da man verschiedene Dinge braucht um überhaupt etwas angezeigt zu bekommen:

  • einen leeren Szenengraphen (VirtualUniverse)
  • ein Fenster zum Rendern (Frame + 3D-Canvas)
  • ein Objekt zum Anzeigen (z.B. eine farbige Box)
  • eine Lichtquelle - damit man die Kiste überhaupt sehen kann (ist in unserem Beispiel schon in die ColorBox integriert)
  • einen Betrachter mit Standort und Blickrichtung (Sie sind ja jetzt in 3D)
  • eine BoundingSphere (Hüllkugel), da Java3D so auf Optimierung aus ist, daß nur Dinge, die in der aktuellen Szene (sprich: der BoundingSphere) enthalten sind überhaupt gezeichnet werden. Andere Optimierungsschritte sind z.B. das Setzen der Capability-Bits in der Transformgruppe, da zur Laufzeit nur Dinge verändert werden können (z.B. mittels Animation), deren Capability-Bits vorher gesetzt wurden.

Man teilt die Bestandteile nun in Content-Branch (alles was angezeigt werden soll, sprich: Lichtquelle und Box) und View-Branch (die Beschreibung eines virtuellen Betrachters und die Umsetzung auf den Rendercanvas und den Frame) ein. Das hört sich zuerst einmal relativ aufwendig an, ermöglicht aber später die einfache und elegante Erweiterung um mehrere "Levels" als Content-Braches und mehrere "Spieler" als View-Branches.

Das erste Beispiel besteht nun aus drei Teilen:

  • createScenegraph(): baut den Content-Branch auf (Colorbox + Animation + Transformation)
  • HelloWorld: Konstruktor - baut den View-Branch (vordefiniertes SimpleUniverse) auf und integriert den Content-Branch aus createSceneGraph()
  • main-Methode: ruft den Konstruktor HelloWorld() im Fenster MainFrame auf (dieses läßt sich als Applet oder als Application betreiben)

Ein erstes Beispiel (eine bunte Kiste mit eingebauter Lichtquelle) könnte nun so aussehen:

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
 
public class HelloUniverse extends Applet
{
  public BranchGroup createSceneGraph()
  {
      BranchGroup objRoot = new BranchGroup();
      TransformGroup objTrans = new TransformGroup();
      objTrans.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
      objRoot.addChild( objTrans );
      objTrans.addChild( new ColorCube( 1.0 ));
      Transform3D yAxis = new Transform3D();
      Alpha rotationAlpha = new Alpha( -1, 4000 );
      RotationInterpolator rotator= new RotationInterpolator(rotationAlpha, objTrans, yAxis, 0.0f, (float) Math.PI*2.0f);
      BoundingSphere bounds = new BoundingSphere( new Point3d( 0.0,0.0,0.0 ), 100.0 );
      rotator.setSchedulingBounds( bounds );
      objRoot.addChild( rotator );
      objRoot.compile();
      return objRoot;
  }
  public HelloUniverse()
  {
      setLayout(new BorderLayout());
      GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
      Canvas3D c=new Canvas3D( config );
      add( "Center", c );
      SimpleUniverse u=new SimpleUniverse( c );
      u.getViewingPlatform().setNominalViewingTransform();
      BranchGroup scene=createSceneGraph();
      u.addBranchGraph( scene );
  }
  public static void main( String[] args )
  {
      new MainFrame( new HelloUniverse(), 600, 600 );
  }
}

Nun ein Beispiel mit Mausnavigation und einer weissen Box, die man mit der Maus drehen kann. Dazu wird im Unterschied zum ersten Beispiel einfach die Methode createSceneGraph() angepaßt. Damit Java3D das MouseBehavior und die Box kennt muß es noch oben in die import-Liste eingetragen werden mittels:

  import com.sun.j3d.utils.behaviors.mouse.MouseRotate;  //  Mousebehavior
  import com.sun.j3d.utils.geometry.*;                   //  Box
  ...
public BranchGroup createSceneGraph()
{
    BranchGroup objRoot = new BranchGroup();
    Transform3D zTrans = new Transform3D( );
    zTrans.set( new Vector3f( 0.0f,0.0f,-10.0f ) );
    TransformGroup objTrans = new TransformGroup( zTrans  );
    objRoot.addChild( objTrans );
      // Box erzeugen und einhängen 
    Box prim      = new Box();
    objTrans.addChild( prim );
      // BoundingSpere für Mousebehavior und Lichtquelle erzeugen
    BoundingSphere bounds = new BoundingSphere( new Point3d(0.0,0.0,0.0), 100.0 );
      // Mouse-Rotation-Behavior ersetzen und in Transformgruppe einhängen + Capabilitybits setzen
    MouseRotate behavior = new MouseRotate( objTrans );
    objTrans.addChild( behavior );
    behavior.setSchedulingBounds( bounds );
    objTrans.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
    objTrans.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
      //Lichtquelle erzeugen und in Szenengraphen hängen
    Color3f lColor1 = new Color3f(1.0f, 1.0f, 1.0f);
    Vector3f lDir1  = new Vector3f( 0f, 0f, -1.0f);
    DirectionalLight lgt1 = new DirectionalLight( lColor1, lDir1 );
    lgt1.setInfluencingBounds( bounds );
    objRoot.addChild( lgt1 );
      // Content-Branch optimieren und zurückgeben
    objRoot.compile();
    return objRoot;
  }