# Java3D-Tutorial, die zweite.



## Illuvatar (20. Mai 2004)

Nachdem hoon seine Starthilfe nicht mehr fortzuführen scheint, hab ich mir gedacht, ich schreib auch mal so was in der Art.
Ich werde allerdings noch einmal von vorne anfangen, und mich enger an das englische Java3D-Tutorial von Sun halten.
Bei den Tutorials will ich auch das hier erwähnen, woran Oxygenic mich erinnert hat:


			
				Oxygenic hat gesagt.:
			
		

> Btw: http://www.javacore.de/tutorials.php



Let's start:

*1. Installation*
Ihr benötigt natürlich ein Java-SDK und ein Java3D-SDK. Die neueste Version ist 1.3.1, ob noch mehr kommt, ist fraglich. Die API-Dokumentation ist natürlich auch nützlich.
Das Java3D-SDK gibt es in zwei Versionen: OpenGL und DirectX. Ich habe die Erfahrung gemacht, dass DirectX performancemäßig um einiges besser ist, aber OpenGL ist eben plattformunabhängig. Diese Beschreibung ist über DirectX, OpenGL ist aber sehr ähnlich.

*2. Grundlagen*
*Die Pakete, die mitgeliefert werden:*
javax.media.j3d: Das wichtigste Paket, in der API dokumentiert, enthält die grungliegenden Klassen.
javax.vecmath: Enthält Klassen für 2- bis 4-Dimensionale Punkte/Farben/Vectoren/Matrizen u.ä., auch in der API dokumentiert.
com.sun.j3d.utils und Unterpakete: Enthält viele nützliche Hilfsklassen, allerdings nur hier dokumentiert (den Link habe ich dank Oxygenic hier gefunden), und das recht schlecht.

*Der SceneGraph:*
In jedem Java3d-Programm gibt es ein javax.media.j3d.Canvas3D (bei allen Klassen muss man aufpassen, das D von 3d groß zu schreiben). Dieses Canvas3D besitzt einen javax.media.j3d.View. Dieser gehört zu einem javax.media.j3d.VirtualUniverse. Damit man nicht in jedem Programm ein VirtualUniverse machen muss, gibt es die Klasse com.sun.j3d.utils.universe.SimpleUniverse. Wenn man dieses benutzt, muss man nicht mehr machen, als ein neues SimpleUniverse zu instanzieren, dieses erhält im Konstruktor ein Canvas3D, der View wird automatisch hinzugefügt. Ein SimpleUniverse kann allerdings nur ein View (also sozusagen eine Kamera) im Geschehen haben, in den Java XTools gibt es eine Klasse MultiUniverse, mit der auch mehrere Views möglich sind, dazu komme ich später nochmal zurück.
Der SceneGraph ist dann das, was sämtliche Objekte im VirtualUniverse enthält. Ein VirtualUniverse hat mehrere javax.media.j3d.Locales, an die javax.media.j3d.BranchGroups gehängt werden können.
BranchGroup ist von javax.media.j3d.Node abgeleitet, d.h. es kann in der parent-child-Beziehung eines SceneGraphs/einer Locale hinzugefügt werden. Jedes Node kann nur ein Parent-Node haben; außerdem ist BranchGroup aus javax.media.j3d.Group abgeleitet, Group ist ein Node, welches child-Nodes haben kann, BranchGroup ist eine Standard-Implementierung von Group, BranchGroup hat keine speziellen Eigenschaften. BranchGroups haben die Methode detach, mit der sie (und alle Kindknoten) äußerst komfortabel entfernt werden können.
Genug damit fürs erste.   Kapiert? :wink:

*Capabilities:*
Ein wichtiges Konzept in Java3d sind die Capabilities. Bevor ein SceneGraph live, also auf dem Bildschirm, ist können Capabilities gesetzt werden, die später bestimmte Veränderungen zulassen.
Sie können über die Methode SceneGraphObject(Node ist aus dieser Klasse abgeleitet)#setCapability bzw clearCapability gesetzt werden. |-Verknüpfungen funktionieren _nicht_, jedes Capability-Bit muss einzeln gesetzt werden.
Wenn bestimmte Capabilities nicht gesetzt sind, kann Java3d Optimierungen vornehmen (z.B. mehrere Groups zu einer zusammenfassen).

*Das erste Programm:*
Damit kann man jetzt das erste Programm schreiben (es ist ein ähnlich wie das HelloJava3Da-Programm)(Ich merke gerade, hoon hat damals übrigens ein ziemlich ähnliches Beispielprogramm geschrieben):

```
import javax.swing.*;
import java.awt.event.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.universe.*;  //SimpleUniverse
import com.sun.j3d.utils.geometry.ColorCube;  //Ein vorgefertigtes Test-SceneGraphObject, das hier benutzt werden wird.

public class First3DProgram extends JFrame implements ActionListener  //JFrame um Vermischung von Swing <-> Canvas3D (AWT) zu zeigen
{
  public static final long serialVersionUID = 121121112l;  //Für Java1.5-Unterstützung
  //Konstruktor
  public First3DProgram()
  {
    super ("Ein ColorCube");
    setSize (500, 500);
    setLocationRelativeTo (null);  //zentrieren
    setDefaultCloseOperation (DO_NOTHING_ON_CLOSE);  //Schließen soll im Menu geschehen
    JPopupMenu.setDefaultLightWeightPopupEnabled (false);  //Damit das Menu funktioniert, lassts mal zum Test weg
    setCloseMenuBar (this);
    //Jetzt kommt der 3D-Teil
    Canvas3D c3d = new Canvas3D (SimpleUniverse.getPreferredConfiguration());  //So am besten
    SimpleUniverse simpleU = new SimpleUniverse (c3d);  //Das VirtualUniverse, hier wird auch schon der View hinzugefügt
    BranchGroup bg = new BranchGroup();  //Haupt-BranchGroup
    bg.addChild (new ColorCube (0.4f/*Größe*/));  //ColorCube wird hinzugefügt
    bg.compile();  //Optimiert das Rendern
    simpleU.addBranchGraph (bg);  //Fügt den SceneGraph hinzu
    simpleU.getViewingPlatform().setNominalViewingTransform();  //Versetzt die Kamera so, dass man gleich etwas sehen kann
    //So einfach war das :)
    add (c3d);  //Vor 1.5: getContentPane().add
    setVisible (true);
  }
  //Beenden-Menu
  private void setCloseMenuBar (JFrame f)
  {
    JMenuBar jmb = new JMenuBar();
    JMenu jm = new JMenu ("Datei");
    jmb.add (jm);
    JMenuItem close = new JMenuItem ("Beenden");
    jm.add (close);
    close.addActionListener (this);
    f.setJMenuBar (jmb);
  }
  public void actionPerformed (ActionEvent evt)
  {
    System.exit (0);
  }
  //Startmethode
  public static void main (String[]args)
  {
    new First3DProgram();  //An "Insider": Ich werde nicht auf die Klasse MainFrame eingehen.
  }
}
```

Na gut, sehr interessant ist das Programm noch nicht, aber wenn man den Würfel dann dreht, sieht das schon ganz gut aus.
Ein kleiner Vorgeschmack:
Fügt mal vor der compile-Methode

```
OrbitBehavior orbit = new OrbitBehavior(c3d, OrbitBehavior.REVERSE_ALL);  //OrbitBehavior liegt in dem Paket com.sun.j3d.utils.behaviors.vp
      orbit.setSchedulingBounds (new BoundingSphere ());
      simpleU.getViewingPlatform().setViewPlatformBehavior (orbit);
```
ein, dann könnt ihr den Würfel mit der Maus drehen.

Ich werde jetzt die nächsten zwei Wochen weg sein, aber dann mache ich natürlich weiter.

In der Hoffnung, das Java3D-Forum damit etwas anzuregen

Illuvatar


----------



## Illuvatar (20. Mai 2004)

Na ja, mach doch heute noch bissle was :wink: :

*3. TransformGroups*
Wenn Bewegung oder einfach nur eine Anordnung der SceneGraphObjects ins Spiel kommen soll, sind javax.media.j3d.TransformGroups das wichtigste. Sie werden im SceneGraph zwischen eine BranchGroup und Leaves(=Nodes, die keine Childnodes haben können) / Groups platziert. Eine TransformGroup hat ein javax.media.j3d.Transform3D, das bestimmte Eigenschaften wie Translation / Verschiebung im 3-dimensionalen Raum und Rotation / Drehung hat, das mit setTransform gesetzt werden kann. Transform3Ds können zudem über die Methode Transform3D#mul (Transform3D) zusammengefasst werden. Es bietet sich allerdings an, z.B., sowohl für X-, als auch für Y- als auch für Z-Drehung als auch für Verschiebung eigene TransformGroups zu machen. Das ist übersichtlicher, und wenn kein ALLOW_TRANSFORM_READ bzw. ALLOW_TRANSFORM_WRITE Capability gesetzt ist, werden die Transform3Ds von der compile()-Methode in eine TransformGroup geschrieben.
Übrigens: bei der Drehung ist Math.PI eine halbe Drehung.
Dann können wir ja den Code etwas ändern:

```
//Jetzt kommt der 3D-Teil
    Canvas3D c3d = new Canvas3D (SimpleUniverse.getPreferredConfiguration());  //So am besten
    SimpleUniverse simpleU = new SimpleUniverse (c3d);  //Das VirtualUniverse, hier wird auch schon der View hinzugefügt
    BranchGroup scene = new BranchGroup();  //Haupt-BranchGroup, SceneGraph
    TransformGroup rotX = new TransformGroup();
    TransformGroup rotY = new TransformGroup();
    TransformGroup trans = new TransformGroup();
    Transform3D t3d_rotx = new Transform3D();
    t3d_rotx.rotX (Math.PI / 4);  //Achtel-Drehung
    rotX.setTransform (t3d_rotx);
    Transform3D t3d_roty = new Transform3D();  //Nur X- und Y-Achse
    t3d_roty.rotY (Math.PI / 4);  //Achtel-Drehung
    rotY.setTransform (t3d_roty);
    scene.addChild (rotX);  //Erstellung des parent-child-Konstruktes
    rotX.addChild (rotY);
    Transform3D t3d_trans = new Transform3D();
    t3d_trans.setTranslation (new Vector3f (0, 0.3f, 0));  //javax.vecmath.Vector3f, Zahlen in "Meter" (Haupteinheit in Java3d)
    //Wie man sieht, geht die Y-Achse von unten nach oben
    trans.setTransform (t3d_trans);
    rotY.addChild (trans);
    trans.addChild (new ColorCube (0.4f/*Größe*/));  //ColorCube wird hinzugefügt
    scene.compile();  //Optimiert das Rendern
    simpleU.addBranchGraph (scene);  //Fügt den SceneGraph hinzu
    simpleU.getViewingPlatform().setNominalViewingTransform();  //Versetzt die Kamera so, dass man gleich etwas sehen kann
    //Nicht mehr ganz so simpel, aber auch nicht schwer, oder? :)
```

Transform3D hat noch viel mehr Methoden, die kann man aber in der API nachlesen. Interessant ist vor allem noch Transform3D#lookAt (Point3d, Point3d, Vector3d). Wobei mir auffällt: Point3*d* is richtig, in den javax.vecmath-Klassen ist das d kleingeschrieben.

*Behaviors:*
Durch Behaviors wird dem Programmierer die Möglichkeit gegeben, die virtuelle Welt zu verändern. Sun rät dazu, dies nie außerhalb von Behaviors zu tun. Dies kann, nach meiner Erfahrung, zu Exceptions irgendwo im J3D-Thread führen, durch die das ganze Programm crasht, oder massiv Performance kosten. Ein Behavior funktioniert so:
Behavior ist auch aus javax.media.j3d.Node abgeleitet, d.h. man kann ihn wie jedes beliebige andere Objektin den Scenegraph hängen. Um genau zu sein muss man das sogar, damit der Behavior arbeitet, die Bedingungen werden später nochmal zusammengefasst. Behavior hat zwei abstrakte Methoden, die überschrieben werden wollen. Die erste ist initialize(). Diese Methode wird einmal aufgerufen, sobald *1.* der Behavior "live", also in den SceneGraph eingehängt wird und *2.* die ViewPlatform, also die Kamera, innerhalb der *Scheduling*Bounds des Behaviors ist (setSchedulingBounds, nicht setBounds, häufiger Fehler!). Hier können Initialisierungen vorgenommen werden. Außerdem wird eine WakeupCondition festgelegt, die besagt, wann der Behavior das nächste Mal aufgerufen werden soll. Im Normalfall wird man hier die 14 Subklassen von WakeupCriterion benötigen, die anderen WakeupConditions dienen dazu, WakeupCriteria zu verknüpfen. Die Criteria beinhalten z.B. Zeit in Millisekunden, abgelaufene Frames (Zeichnungen des Canvas3D), Kollisionen oder AWTEvents. Genauer kann man das hier nachlesen. Wenn nun also die WakeupCondition eintritt (und die beiden obigen Punkte erfüllt sind), wird die processStimulus-Methode von J3D-Thread aufgerufen. Es empfiehlt sich also nicht, hier lange Pausen einzubauen, die Methode muss schnell zurückkehren. Am Ende muss wieder eine WakeupCondition gesetzt werden.

Es gibt allerdings auch bereits viele vorgefertigte Behavior-Klassen. Diese teilen sich in 3 Hauptgruppen auf:

Billboard:

Rotiert eine TransformGroup anhand einer Achse oder eines Punktes, so dass bei dem Hauptview immer die selbe Seite zu sehen ist. Btw. für Fortgeschrittene: Es gibt auch die Klasse OrientedShape3D, die einen Shape3D darstellt, der eben dieses Verhalten hat.

LOD (Level of detail):

Verändern die Detailstufe einer TransformGroup auf Grund beliebiger Parameter. Im Moment ist die einzige Subklasse DistanceLOD, die die Veränderung auf Grund der Enfernung vornimmt.

Interpolatoren:

Interpolators verändern in der Zeit, die von einem javax.media.j3d.Alpha (da kommt noch was) vorgegeben wird, eine TransformGroup, zum Beispiel in Ort, Drehung oder Farbe.
Am besten kann ich das mit einem Beispiel erklären: Ein sich drehender ColorCube:

```
import javax.swing.*;
import java.awt.event.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.universe.*;  //SimpleUniverse
import com.sun.j3d.utils.geometry.ColorCube;  //Ein vorgefertigtes Test-SceneGraphObject, das hier benutzt werden wird.

public class My3DProgram extends JFrame implements ActionListener  //JFrame um Vermischung von Swing <-> Canvas3D (AWT) zu zeigen
{
  public static final long serialVersionUID = 121121112l;  //Für Java1.5-Unterstützung
  //Konstruktor
  public My3DProgram()
  {
    super ("Ein ColorCube");
    setSize (500, 500);
    setLocationRelativeTo (null);  //zentrieren
    setDefaultCloseOperation (DO_NOTHING_ON_CLOSE);  //Schließen soll im Menu geschehen
    JPopupMenu.setDefaultLightWeightPopupEnabled (false);  //Damit das Menu funktioniert, lassts mal zum Test weg
    setCloseMenuBar (this);
     //Jetzt kommt der 3D-Teil
    Canvas3D c3d = new Canvas3D (SimpleUniverse.getPreferredConfiguration());  //So am besten
    SimpleUniverse simpleU = new SimpleUniverse (c3d);  //Das VirtualUniverse, hier wird auch schon der View hinzugefügt
    BranchGroup scene = createSceneGraph();
    simpleU.addBranchGraph (scene);  //Fügt den SceneGraph hinzu
    simpleU.getViewingPlatform().setNominalViewingTransform();  //Versetzt die Kamera so, dass man gleich etwas sehen kann
    //Nicht mehr ganz so simpel, aber auch nicht schwer, oder? :)
    add (c3d);  //Vor 1.5: getContentPane().add
    setVisible (true);
  }
  public BranchGroup createSceneGraph() {  //Methode aus Java3D-Examples von Sun
    BranchGroup objRoot = new BranchGroup();
    TransformGroup objSpin = new TransformGroup();
    objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    objRoot.addChild(objSpin);
    objSpin.addChild(new ColorCube(0.1));
    Alpha rotationAlpha = new Alpha(-1, 4000); //Unendlich oft drehen, einmal in 4000 ms
    RotationInterpolator rotator =
       new RotationInterpolator(rotationAlpha, objSpin);
    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100);  //Unbedingt benötigt: Die BoundingSphere
    rotator.setSchedulingBounds(bounds);
    objSpin.addChild(rotator);
    objRoot.compile ();
    return objRoot;
  }
  //Beenden-Menu
  private void setCloseMenuBar (JFrame f)
  {
    JMenuBar jmb = new JMenuBar();
    JMenu jm = new JMenu ("Datei");
    jmb.add (jm);
    JMenuItem close = new JMenuItem ("Beenden");
    jm.add (close);
    close.addActionListener (this);
    f.setJMenuBar (jmb);
  }
  public void actionPerformed (ActionEvent evt)
  {
    System.exit (0);
  }
  //Startmethode
  public static void main (String[]args)
  {
    new My3DProgram();
  }
}
```
Das wichtigste an Interpolatoren ist eigentlich das Alpha-Objekt. Es macht eigentlich nicht mehr, als anhand der Zeit einen Wert von 0 bis 1 darzustellen, den der Interpolator dann umsetzt. Alpha bietet vier Konstruktoren. Dieser beinhaltet alle möglichen Parameter:


> Alpha(int loopCount, int mode, long triggerTime, long phaseDelayDuration, long increasingAlphaDuration, long increasingAlphaRampDuration, long alphaAtOneDuration, long decreasingAlphaDuration, long decreasingAlphaRampDuration, long alphaAtZeroDuration)


In den anderen werden für die fehlenden Parameter Standardwerte eingesetzt. loopCount ist die Anzahl, wie oft das Objekt ausgeführt werden soll. mode kann festelegen, ob der Wert nur abnehmen, nur zunehmen oder beides abwechselnd soll. Dafür gibt es die Konstanten INCREASING_ENABLE und DECREASING_ENABLE, die geORed werden können ( | Operator). triggerTime und phaseDelayDuration ergeben zusammen die Zeit, die vor dem Ausführen gewartet wreden soll. IncreasingAlphaDuration ist die Zeit, die das Objekt von 0 bis 1 braucht und alphaAtOneDuration ist die Zeit, die es vor dem Runterzählen wartet. increasingAlphaRampDuration ist etwas trickreich, dieser Wert gibt die Zeit an, in der das Alpha-Objekt am anfang langsam beschleunigt um am Ende ebenso abbremst. Für die decreasing-Werte gilt das selbe, nur andersherum.

*4. 2d-Graphik*
Es ist auch möhlich, über/unter die 3D-Graphik mit javax.media.j3d.J3DGraphics2D zu zeichnen, welche aus java.awt.Graphics2d abgeleitet sind. [edit]Man bekommt sie über Canvas3d#getGraphics2D()[/edit] Dies muss in den Methoden preRender() oder postRender() aus Canvas3D geschehen, deshalb wird es nötig, eine eigene Klasse aus Canvas3D abzuleiten. Nach dem Zeichnen sollte die Methode J3DGraphics2D#flush(true) aufgerufen werden.
Siehe auch hier.

Genug für heute.


----------



## Illuvatar (9. Jun 2004)

Es geht weiter... :meld: 

*5. javax.media.j3d.Shape3D*
Shape3D ist aus Leaf abgeleitet. Sämtliche sichtbaren Vordergrund-Objekte der Szene sind Shape3Ds (übrigens auch ein ColorCube). Ein Shape3D hat sowohl einen Verweis auf eine javax.media.j3d.Geometry, die sozusagen das "Drahtgittermodell" darstellt, als auch auf eine javax.media.j3d.Appearance (Vorsicht beim Schreiben!), die das äußere Erscheinungsbild darstellt, aber nicht vorhanden sein muss.

*Appearance*
Eine Appearance hat folgende Merkmale:


			
				Java3dTutorial von Sun hat gesagt.:
			
		

> · PointAttributes
> · LineAttributes
> · PolygonAttributes
> · ColoringAttributes
> ...


wobei im Moment nur die oberen 6 Punkte wichtig sind. Diese 6 Klassen befinden sich alle im Paket javax.media.j3d und können über entsprechende Methoden zu einer Appearance hinzugefügt werden (Appearance#addLineAttributes, Appearance#addPolygonAttributes usw), die Appearance wird entweder über den Konstruktor oder über die Methode setAppearance zum Shape3D hinzugefügt.
Die Attribute-Klassen im Einzelnen (nur die einzelnen Variablen der Klassen werden beschrieben, wie die Methoden zur Veränderung heißen, kann in der API nachgelesen werden, normalerweise sind es einfache setter-Methoden):
_PointAttributes_
float pointSize: Die Größe der Punkte, funktioniert nicht unter DirectX
boolean state: Zeigt an, ob Antialiasing auf die Punkte angewandt werden soll. Nur wichtig bei Punktgrößen > 1.0f
_LineAttributes_
float pointSize: Dicke der Linien, funktioniert nicht unter DirectX
boolean state: Zeigt an, ob Antialiasing auf die Linien angewandt werden soll.
int linePattern: PATTERN_SOLID (standard), PATTERN_DASH, PATTERN_DOT, oder PATTERN_DASH_DOT, Beschreibt, wie die Pixel einer Linie gezeichnet werden sollen
_PolygonAttributes_
int cullFace: "cull" bedeutet "nicht rendern". Kann auf eine der folgenden Konstanten gesetzt werden: CULL_FRONT, CULL_BACK (standard), oder CULL_NONE
int polygonMode: folgende Konstanten stehen zur Auswahl: POLYGON_POINT (nur Punkte werden angezeigt), POLYGON_LINE (Punkte und Linien werden angezeigt), oder POLYGON_FILL (standard, Punkte, Linien und Flächen werden angezeigt)
float polygonOffset: Das Shape3D wird um so viel auf der z-Achse des VirtualUniverse verschoben
boolean backFaceNormalFlip: Wenn CULL_BACK nicht aktiv ist, werden die "normals" der Rückseite umgekehrt (mehr dazu später)
_ColoringAttributes_
Color3f color: Die Farbe
int shadeModel: SHADE_GOURAUD, SHADE_FLAT, FASTEST oder NICEST
_TransparencyAttributes_
float tVal: 0.0 ist total undurchsichtig, 1.0 total transparent
int tMode: BLENDED, SCREEN_DOOR, FASTEST, NICEST, oder NONE

*Geometry-Hilfsklassen*
Um ganz einfach Szeneninhalt zu erstellen, gibt es vier "Geometric Utility Classes" im Paket com.sun.j3d.utils.geometry. Sie können wie ein Shape3D, dass bereits eine Geometry hat, verwendet werden. Es sind die Klassen Box (Quader), Cone (Kegel), Cylinder (Zylinder) und Sphere (Kugel).
Ein Beispiel zur Verdeutlichung:

```
import javax.swing.*;
import java.awt.event.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.universe.*;  //SimpleUniverse
import com.sun.j3d.utils.geometry.*;  //Die Geometry-Utility-Klassen
import static java.awt.Color.*;  //Vor 1.5 import java.awt.Color;

public class Jojo extends JFrame implements ActionListener
{
  public static final long serialVersionUID = 121121112l;  //Für Java1.5-Unterstützung
  //Konstruktor
  public Jojo()
  {
    super ("Ein Jojo");
    setSize (500, 500);
    setLocationRelativeTo (null);  //zentrieren
    setDefaultCloseOperation (DO_NOTHING_ON_CLOSE);  //Schließen soll im Menu geschehen
    JPopupMenu.setDefaultLightWeightPopupEnabled (false);  //Damit das Menu funktioniert
    setCloseMenuBar (this);
     //Jetzt kommt der 3D-Teil
    Canvas3D c3d = new Canvas3D (SimpleUniverse.getPreferredConfiguration());  //So am besten
    SimpleUniverse simpleU = new SimpleUniverse (c3d);  //Das VirtualUniverse
    BranchGroup scene = createSceneGraph(); //In eigene Methode auslagern
    simpleU.addBranchGraph (scene);  //Fügt den SceneGraph hinzu
    simpleU.getViewingPlatform().setNominalViewingTransform();  //Versetzt die Kamera so, dass man gleich etwas sehen kann
    //Bewegt die Kamera etwas weg
    TransformGroup camTG = simpleU.getViewingPlatform().getViewPlatformTransform();  //TransformGroup steht für Kamera
    Transform3D t3d = new Transform3D();
    t3d.setTranslation (new Vector3f (0, 0, 7));  //neue Veränderung
    Transform3D t3d2 = new Transform3D();
    camTG.getTransform (t3d2);  //alte Veränderung
    t3d.mul (t3d2);  //beide Veränderungen zusammen
    camTG.setTransform (t3d);
    //Antialiasing aktivieren
    c3d.getView().setSceneAntialiasingEnable (true);
    //3D-Teil Ende. Soweit verstanden?
    add (c3d);  //Vor 1.5: getContentPane().add
    setVisible (true);
  }
  public BranchGroup createSceneGraph() {
    BranchGroup objRoot = new BranchGroup();
    TransformGroup objSpin = new TransformGroup();
    objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    objRoot.addChild(objSpin);
    Alpha rotationAlpha = new Alpha(-1, 8000); //Das gesamte unendlich oft drehen, einmal in 8000 ms
    RotationInterpolator rotator =
       new RotationInterpolator(rotationAlpha, objSpin);
    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100);
    rotator.setSchedulingBounds(bounds);
    objSpin.addChild(rotator);
    //Hier wirds neu
    //linke Hälfte
    TransformGroup leftCone = getCone (new Color3f (RED/*vor 1.5: Color.RED*/), true);
    objSpin.addChild (leftCone);
    //rechte Hälfte
    TransformGroup rightCone = getCone (new Color3f (BLUE/*vor 1.5: Color.BLUE*/), false);
    objSpin.addChild (rightCone);  //Cones stecken übrigens ineinander
    objRoot.compile ();
    return objRoot;
  }
  private TransformGroup getCone (Color3f col, boolean leftSide)
  {
    Cone cone = new Cone (1/*radius*/, 3/*height*/); //default wäre: height: 2.0, radius: 1.0
    Appearance coneApp = new Appearance();
    coneApp.setColoringAttributes (new ColoringAttributes (col, ColoringAttributes.NICEST));
    coneApp.setPolygonAttributes (new PolygonAttributes (PolygonAttributes.POLYGON_FILL/*Testet auch mal _LINE und _POINT*/, PolygonAttributes.CULL_BACK, 0));
    coneApp.setTransparencyAttributes (new TransparencyAttributes (TransparencyAttributes.NICEST, 0.3f/*Hier könnt ihr auch mal rumspielen*/));
    //Wenn ich bei den TransparencyAttributes zuwenig nehme, gibt es Darstellungsfehler
    cone.setAppearance (coneApp);
    Transform3D trans = new Transform3D();
    trans.rotZ (Math.PI / (leftSide ? -2 : 2));
    TransformGroup TG = new TransformGroup (trans);
    TransformGroup move = new TransformGroup();
    PositionPathInterpolator pi = new PositionPathInterpolator
(new Alpha (-1, 3000), move, new Transform3D(), new float[]{0, 0.5f, 1}, new Point3f[]{new Point3f (), new Point3f (leftSide ? 2 : -2, 0, 0), new Point3f ()});
 //Vorgegebener Behavior, einfach API schauen, im Paket javax.media.j3d
    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100);
    pi.setSchedulingBounds(bounds);
    move.addChild (pi);
    move.setCapability (TransformGroup.ALLOW_TRANSFORM_WRITE);
    move.addChild (cone);
    //Der jetzt folgende Teil wird nächstes mal erklärt, es ist eine drei-D-Linie, die sich zwar mitbewegt, aber so lang ist, dass man es nicht merkt :)
    if (leftSide){  //eine reicht
      LineArray la = new LineArray (2, LineArray.COORDINATES | LineArray.COLOR_3);
      la.setCoordinate (0, new Point3f (-20, 0, 0));
      la.setCoordinate (1, new Point3f ());
      la.setColor (0, new Color3f (WHITE/*bzw. Color.WHITE*/));
      la.setColor (1, new Color3f (WHITE/*bzw. Color.WHITE*/));
      move.addChild (new Shape3D (la));
    }
    TG.addChild (move);
    return TG;
  }
  //Beenden-Menu
  private void setCloseMenuBar (JFrame f)
  {
    JMenuBar jmb = new JMenuBar();
    JMenu jm = new JMenu ("Datei");
    jmb.add (jm);
    JMenuItem close = new JMenuItem ("Beenden");
    jm.add (close);
    close.addActionListener (this);
    f.setJMenuBar (jmb);
  }
  public void actionPerformed (ActionEvent evt)
  {
    System.exit (0);
  }
  //Startmethode
  public static void main (String[]args)
  {
    new Jojo();
  }
}
```

So, das war erstmal lang genug.


----------



## Oxygenic (9. Jun 2004)

Ein paar Review-Kommentare (nur so beim Überfliegen aufgefallen):

Zu den Capabilities wäre anzumerken, dass die nicht nur irgend ein Gag sind, die den User grundlos dazu zwingen, diese zu setzen. Vielmehr führen die Optimierungen, die Java3D auf Basis der nicht gesetzten Capabilities macht, zu spürbaren Geschwindigkeitsvorteilen. Auch empfehle ich einen Blick auf setCapabilityIsFreqent().

Ob jemand die Behaviors so ohne ausführliche Erklärung versteht wage ich zu bezweifeln.

Die TransformGroup-Methode mul() heißt übrigens nicht deswegen mul(tipliziere), weil damit eine Addition ausgeführt wird. Über die Empfehlung, für X-, Y-, und Z-Rotationen je eine eigene TransformGroup einzurichten kann man trefflich streiten. Der Sinn dahinter ist mir nicht klar, auf alle Fälle kostet es Speicher und Compilezeit, denn die J3D-internen Optimierungen fassen sie dann eh wieder zusammen.

PointAttributes/LineAttributes: dass diese nur bei der Darstellung von Punktwolken / Drahtgittermodellen Sinn machen, sollte vielleicht erwähnt werden.

Primitives als Geometry-Hilfsklassen unter der Überschrift Shape3D zu erwähnen ist IMHO ziemlich verwirrend. Sie sind nämlich in keinster Weise mit den Shape3Ds verwandt und auch an ihre Geometriedaten kommt man nicht so ohne weiteres - schon gar nicht mit den Methoden die von Shape3D her bekannt sind.


----------



## Illuvatar (9. Jun 2004)

Oxygenic hat gesagt.:
			
		

> Zu den Capabilities wäre anzumerken, dass die nicht nur irgend ein Gag sind, die den User grundlos dazu zwingen, diese zu setzen. Vielmehr führen die Optimierungen, die Java3D auf Basis der nicht gesetzten Capabilities macht, zu spürbaren Geschwindigkeitsvorteilen. Auch empfehle ich einen Blick auf setCapabilityIsFreqent().


Hab ich doch geschrieben


			
				Illuvatar hat gesagt.:
			
		

> Wenn bestimmte Capabilities nicht gesetzt sind, kann Java3d Optimierungen vornehmen (z.B. mehrere Groups zu einer zusammenfassen).





			
				Oxygenic hat gesagt.:
			
		

> Ob jemand die Behaviors so ohne ausführliche Erklärung versteht wage ich zu bezweifeln.


Ich, beim erneuten Durchlesen, auch, werd ich noch mal überarbeiten  



			
				Oxygenic hat gesagt.:
			
		

> Die TransformGroup-Methode mul() heißt übrigens nicht deswegen mul(tipliziere), weil damit eine Addition ausgeführt wird. Über die Empfehlung, für X-, Y-, und Z-Rotationen je eine eigene TransformGroup einzurichten kann man trefflich streiten. Der Sinn dahinter ist mir nicht klar, auf alle Fälle kostet es Speicher und Compilezeit, denn die J3D-internen Optimierungen fassen sie dann eh wieder zusammen.


Ich hatte gar nicht bewusst an multiplizieren gedacht, addieren fand ich einfach ein passendes Wort dafür. Habs jetzt aber überarbeitet und schreibe jetzt zusammenfassen.
Das mit den Rotationen finde ich übersichtlicher und einfacher, weil die Achsen ja mitrotiert werden, und da sie zusammengefasst werden, hält sich der benötigte Speicher ja wohl auch in Grenzen.



			
				Oxygenic hat gesagt.:
			
		

> PointAttributes/LineAttributes: dass diese nur bei der Darstellung von Punktwolken / Drahtgittermodellen Sinn machen, sollte vielleicht erwähnt werden.


Dafür hast du ja jetzt gesorgt  . Ich hab mich wegen meiner DirectX-Version noch nicht näher damit befasst.



			
				Oxygenic hat gesagt.:
			
		

> Primitives als Geometry-Hilfsklassen unter der Überschrift Shape3D zu erwähnen ist IMHO ziemlich verwirrend. Sie sind nämlich in keinster Weise mit den Shape3Ds verwandt und auch an ihre Geometriedaten kommt man nicht so ohne weiteres - schon gar nicht mit den Methoden die von Shape3D her bekannt sind.


Mir ist keine bessere Überschrift als Shape3D eigefallen, im Englischen hätte ich Easier Content Creation genommen, noch mal nachdenken. Außerdem hatte ich am Anfang noch vor, noch in diesem Teil mit den GeometryArray-Klassen anzufangen, und es somit ein bisschen zu entwirren, aber es war mir dann doch zu viel auf einmal.

Edit: PS: Falls noch jemand Kritik oder Verbesserungsvorschläge hat, darf er/sie gerne posten.


----------



## doop (14. Jul 2005)

Was ist eigentlich mit dem Tutorial - ist es schon bei Teil zwei gestorben?


----------



## maxf (19. Okt 2005)

Gehts noch weiter (Texturen o.ä.):?:  :?:  :?:


----------



## maxf (30. Okt 2005)

Gutes, deutschschprachiges Buch über Java3D ist:
*Michael Pfeiffer  Java 3D 1.3 für Anfänger und Fortgeschrittene.*
Download als .pdf


----------



## Illuminatus0301 (29. Jul 2006)

@ maxf: Danke für das Tutorial. Ich hab es noch nicht ganz gelesen, aber der erste Eindruck davon ist sehr gut!


----------



## Vorbote der Apokalypse (19. Sep 2006)

Danke fürs Forum ich habe sehr lange nach einem Tutorial für java3D gesucht hab aber nichts gefunden.
Ich werd öfter mal hier hingucken


----------

