# [gelöst] Java3D: Unerwünschte Rotation um die 3.Achse vermeiden?



## thewulf00 (17. Jun 2011)

Hallo,

ich rotiere die View um die X- und die Y-Achse gleichzeitig (per Mauseingaben). Aber während das passiert, verändert sich auch die Z-Rotation. Das ist aber nicht erwünscht, wie kann ich das unterdrücken?

Ich habe zweierlei Vorgehen versucht, aber der Teufel steckt wohl in der Mathematik:
[Java=191]currXform.setRotation(new AxisAngle4f(1f, y_angle/x_angle, 0f, x_angle));[/Java]

[Java=191]transformX.rotX(x_angle);
transformY.rotY(y_angle);
currXform.mul(currXform, transformY);
currXform.mul(currXform, transformX);[/Java]

Die Rotation um nur eine Achse funktioniert tadellos, aber sobald ich um beide rotiere, wirkt sich das auf die Z-Achsenrotation aus. Hat jemand eine Idee, wie ich diese Z-Achsenrotation wegrechnen/beseitigen kann?

Hier ein Bild zur Verdeutlichung:





Links ist das Rotieren der View um nur eine einzelne Achse dargestellt.
Rechts die Kombination beider Achsen. Die grünen Striche geben den Ursprung und die Rotationsrichtung an. Das gelbe Viereck zeigt an, in welcher Z-Rotation ich den roten Würfel eigentlich erwartet habe.


----------



## Marco13 (17. Jun 2011)

Hm. Ja. Viel mehr als "so ist das halt" kann man da kaum sagen. Dummerweise macht er halt immer nur die Rotation, die man Programmiert hat. Schon ein Ändern der Reihenfolge, in der die Multiplikationen durchgeführt werden, würde ja ein anderes Ergebnis liefern.

Spontan-Intuitiv aus dem Bauch heraus würde ich sagen, dass man um eine Achse rotieren muss, die Senkrecht auf der Bewegungsrichtung der Maus steht - und den passenden Winkel auszurechnen könnte dann nochmal ein bißchen komplizierter werden. Kannst du ein KSKB posten, so dass man schnell an einer Stelle mögliche Ansätze ausprobieren kann?


----------



## ChrisKu (17. Jun 2011)

> ich rotiere die View um die X- und die Y-Achse gleichzeitig (per Mauseingaben). Aber während das passiert, verändert sich auch die Z-Rotation. Das ist aber nicht erwünscht, wie kann ich das unterdrücken?



Also, streng genommen rotiert Dein Würfel um die X-Achse und die Y-Achse und nicht um die Z-Achse. Und zwar rotiert er um die EIGENE x-Achse und y-Achse und NICHT um die x und y Achsen der virtuellen Welt. Beispiel: Wenn Du den Würfel 45 Grad um die x-Achse drehst, dann kippt damit auch die y-Achse des Würfels (die ursprünglich senkrecht nach oben zeigte) um 45 Grad nach vorne. Wenn Du den Würfel jetzt um 45 Grad um die y-Achse rotierst, dreht sich der Würfel um die geneigte y - Achse usw. So entsteht die Rotation um die z-Achse der virtuellen Welt. (Ganz deutlich: Wenn Du den Würfel erst um 90 Grad um die y-Achse drehst und dann um seine x-Achse entsteht eine Rotation um die z-Achse der virtuellen Welt).

Du kannst das ganze natürlich zurückrechnen in das Koordinatensystem der virtuellen Welt. Einen Link hierzu:

Transform3D (Java 3D API)

Aber vielleicht ist das auch gar nicht erforderlich, je nachdem, was Du vorhast. Statt die Rotationen aufeinander aufzubauen (die Transformationen zu multiplizieren, Zeile 193 und 194 Deines Codes) kannst Du die Rotation auch jedes Mal ganz neu setzen (und damit bezieht sich Deine Rotation immer auf die ursprüngliche Position). Dieses kann zumindest dann eine Lösung sein, wenn Deine Rotationsänderung jeweils in festen Schritten erfolgt. Ich habe das einmal an einem kleinen Testprogramm implementiert, wo die Rotation allerdings mit 4 Buttons gesteuert wird (2 für die beiden Richtungen der x-Achse und 2 für die beiden Richtungen der y-Achse). Mit jedem Click wird dabei die Anzahl der Roationsschritte um die jeweilige Achse inkremendiert bzw. dekrementiert um dann die komplette Rotation zu setzen. Hier das Code Fragment, bei dem ein Würfel in 45 Grad Schritten gedreht wird:


```
public void actionPerformed(ActionEvent e) {
        if (e.getSource() == exitButton) {
            dispose();
            System.exit(0);
        } else if (e.getSource() == this.lRotXButton){
            this.rotX ++;
        } else if (e.getSource() == this.rRotXButton){
            this.rotX --;

        } else if (e.getSource() == this.lRotYButton){
            this.rotY ++;
        } else if (e.getSource() == this.rRotYButton){
            this.rotY --;
        }
        Transform3D t3d = new Transform3D();
        Transform3D t3d2 = new Transform3D();
        t3d.rotX(rotX * Math.PI / 4.0);
        t3d2.rotY(rotY * Math.PI / 4.0);
        t3d.mul(t3d2);
        rotGroup.setTransform(t3d);
    }
```


Dieses Verhalten entspricht dann dem Verhalten, das Du vermutlich erwartest.


----------



## thewulf00 (17. Jun 2011)

Vielen Dank euch beiden!

Ihr habt mir sehr geholfen. Ich habe beim Aufstellen des KSKBs per Timer das Verhalten einfach nicht reproduzieren können, weil ich instinkiv absolute Rotationen genutzt habe, genau wie ChrisKu das deutlich macht.
Ich habe jetzt mein Problemprogramm mit der Maussteuerung von relativer zu absoluter Drehung umgebaut. Anfangs (im processStimulus) wird einfach ein transform.setIdentity() gemacht und dann die gesamte Rotation jeder Achse ausgeführt.

Die Erklärung von ChrisKu ist genau zutreffend auf mein Problem: Ich habe immer (mit jeder Mausbewegung) einen kleinen, relativen Anteil hinzugerechnet - d.h. zusammengefasst wurde der Würfel 100er Male um verschiedene, relative Achsen gedreht.

Vielen Dank - Mein Verständnis ist nun verbessert worden.
Das funktionierende KSKB poste ich dennoch:

*KSKB_RotZ.java*
[Java]package kskb_rotz;

import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.*;

import java.awt.event.*;

import javax.media.j3d.*;
import javax.vecmath.*;

public class KSKB_RotZ
{
    protected Transform3D  transformX = new Transform3D();
    protected Transform3D  transformY = new Transform3D();
    private Transform3D    result     = new Transform3D();
    private float          x_angle    = 0f,
                           y_angle    = 0f;
    private float          x_factor   = 0.1f,
                           y_factor   = 0.1f;
    private TransformGroup objTrans;

    public KSKB_RotZ()
    {
        SimpleUniverse universe = new SimpleUniverse();
        BranchGroup    scene    = createSceneGraph(universe);

        universe.getViewingPlatform().setNominalViewingTransform();
        universe.addBranchGraph(scene);
    }

    private BranchGroup createSceneGraph(SimpleUniverse su)
    {
        objTrans = su.getViewingPlatform().getViewPlatformTransform();

        BranchGroup    objRoot      = new BranchGroup();
        TransformGroup boxContainer = new TransformGroup();
        ColorCube      box          = new ColorCube();
        Transform3D    boxPosition  = new Transform3D();

        boxPosition.setTranslation(new Vector3f(0.0f, 0.0f, -30.0f));
        boxContainer.addChild(box);
        boxContainer.setTransform(boxPosition);
        objRoot.addChild(boxContainer);

        BoundingSphere mouseBounds = new BoundingSphere(new Point3d(), 1000.0);
        MouseNavigator myNavigator = new MouseNavigator(objTrans);

        myNavigator.setSchedulingBounds(mouseBounds);
        objRoot.addChild(myNavigator);

        return objRoot;
    }

    public static void main(String[] args)
    {
        new KSKB_RotZ();
    }
}[/Java]


*MouseNavigator.java*
[Java]package kskb_rotz;

import java.awt.*;
import java.awt.event.*;

import java.util.*;

import javax.media.j3d.*;

public class MouseNavigator extends Behavior implements MouseListener, MouseMotionListener
{
    protected boolean       reset        = false;
    protected boolean           enable   = true;
    double                      x_factor = .01;
    double                      y_factor = .01;
    protected Transform3D       currXform;
    protected WakeupOr          mouseCriterion;
    protected WakeupCriterion[] mouseEvents;

    protected TransformGroup transformGroup;
    protected Transform3D    transformX;
    protected Transform3D    transformY;

    protected int            x, y;
    double                   x_angle, y_angle;
    protected int            x_last, y_last;

    public MouseNavigator(TransformGroup transformGroup)
    {
        super();

        this.transformGroup = transformGroup;
        currXform           = new Transform3D();
        transformX          = new Transform3D();
        transformY          = new Transform3D();
        reset               = true;
    }

    public TransformGroup getTransformGroup()
    {
        return this.transformGroup;
    }

    public void initialize()
    {
        mouseEvents = new WakeupCriterion[3];

	mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_MOVED);
	mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
	mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);

        mouseCriterion = new WakeupOr(mouseEvents);
        wakeupOn(mouseCriterion);

        x       = 0;
        y       = 0;
        x_last  = 0;
        y_last  = 0;
        x_angle = 0;
        y_angle = 0;

        x_factor *= -1;
        y_factor *= -1;
    }

    public void processStimulus(Enumeration criteria)
    {
        WakeupCriterion wakeup;
        AWTEvent[]      events;
        MouseEvent      evt;

        while (criteria.hasMoreElements())
        {
            wakeup = (WakeupCriterion) criteria.nextElement();
            events = ((WakeupOnAWTEvent) wakeup).getAWTEvent();

            if (events.length > 0)
            {
                evt = (MouseEvent) events[events.length - 1];
                doProcess(evt);
            }
        }

        wakeupOn(mouseCriterion);
    }

    void doProcess(MouseEvent evt)
    {
        int id;
        int dx, dy;

        id = evt.getID();

        if (id == MouseEvent.MOUSE_MOVED)
        {
            x  = evt.getX();
            y  = evt.getY();
            dx = x - x_last;
            dy = y - y_last;

            if (!reset)
            {
                x_angle += dy * y_factor;
                y_angle += dx * x_factor;

                currXform.setIdentity();

                transformY.rotY(y_angle);
                transformX.rotX(x_angle);

                currXform.mul(transformY);
                currXform.mul(transformX);

                transformGroup.setTransform(currXform);
            }else{
                reset = false;
            }

            x_last = x;
            y_last = y;
        }
    }    

    public void mouseClicked(MouseEvent e)   {}
    public void mousePressed(MouseEvent e)  {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e)  {}
    public void mouseExited(MouseEvent e)    {}
    public void mouseDragged(MouseEvent e) {}
    public void mouseMoved(MouseEvent e)   {}   
}[/Java]


----------

