# j3d Kamera Rotation durch Tastendruck



## bob_sheknowdas (3. Apr 2012)

Hallo,
im Prinzip beschreibt der Titel mein problem so weit bereits. Ich schaffe es nicht auf Tastendruck mein View um eine Sphere rotieren zu lassen.
ich habe schon eine Weile im Netz gesucht deswegen, aber nur eine Lösung mit Maus gefunden (OrbitBehavior).
Vllt hat ja jemand von euch eine Idee, hier mal ein bischen von meinem Sourcecode, fals ihrs braucht:


```
public class Spiel  extends JPanel implements Runnable{
	public Spiel() {
		setFocusable(true);
		setSize(300, 300);
		setPreferredSize(getSize());
		setLayout(new BorderLayout());
		
		create3D();
		
		Steuerung steuerung = new Steuerung(this);

		gui = new GUI(this);
		gui.getStartButton().addActionListener(steuerung);
		
		addKeyListener(steuerung);
		
		Thread t = new Thread(this);
		t.start();
	}

	private void create3D() {
		Canvas3D c3d = new Canvas3D(SimpleUniverse.getPreferredConfiguration()); 
		SimpleUniverse simpleU = new SimpleUniverse(c3d);
		
		BranchGroup group = new BranchGroup();
		
		group.addChild(create3DObject(new ColorCube(0.5f), 2f,-2f , 0.0f));
		group.addChild(create3DObject(new ColorCube(0.5f), -3f,1f , -0.5f));
		group.addChild(create3DObject(new ColorCube(0.5f), -1.5f,3f , -3.0f));
		group.addChild(create3DObject(new ColorCube(0.5f), 0f,-1f , 2f));

		Sphere sphere = new Sphere(0.5f);
		mainTfGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		trans.setTranslation(new Vector3f(0f,0f,0.0f));
		mainTfGroup.setTransform(trans);
		mainTfGroup.addChild(sphere);
		group.addChild(mainTfGroup);
		
		Color3f light1Color = new Color3f(1.8f, 0.1f, 0.1f);
		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
		Vector3f light1Direction = new Vector3f(1.0f, -3.0f, -12.0f);
		DirectionalLight light1 = new DirectionalLight(light1Color, light1Direction);
		light1.setInfluencingBounds(bounds);
		group.addChild(light1);
		
		camereGroup = simpleU.getViewingPlatform().getViewPlatformTransform();
		camera = new Transform3D();
		camera.setRotation(new AxisAngle4d(-0.07, 0.1, 0.0, 0.12));
		camera.setTranslation(new Vector3f(0.3f, 0.3f, 8f));  
		camereGroup.setTransform( camera );
		
		simpleU.addBranchGraph(group);
		c3d.setFocusable(false);
		
		add(c3d);
	}

	@Override
	public void run() {
		while(true){
			if(right)xloc+=0.05;
			if(left)xloc-=0.05;
			if(up)yloc+=0.05;
			if(down)yloc-=0.05;
			if(speedup&&speed>=-0.13)speed-=0.0015;
			if(slowdown&&speed<=0.03)speed+=0.0015;
			zloc+=speed;
			trans.setTranslation(new Vector3f(-0.5f,yloc,zloc));
			camera.setTranslation(new Vector3f(0.0f, yloc, zloc+8f)); 
			mainTfGroup.setTransform(trans);
			camereGroup.setTransform( camera );
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}	
		}
	}

[...]

}
```

Vielen Dank im Vorraus für Hilfe aller art


----------



## bob_sheknowdas (3. Apr 2012)

Ich hatte noch die Idee, die Transformgroup der Kamera und der Sphere in eine obere Transformgroup reinzustecken und diese zu rotieren (was ich mir einfach vorstelle), aber dummerweise sind Transformgroups nicht schachtelbar...


sry for Doppelpost...


----------



## Marco13 (3. Apr 2012)

Schachteln...? Man kann sie hintereinanderschalten...

Hilft sowas wie Rotate when any key is pressed : Mouse Keyboard Action3DJava ? Wenn nicht, wäre ein KSKB und eine genauere Frage gut.


----------



## bob_sheknowdas (3. Apr 2012)

Also...

Das mit dem Schachteln habe ich nicht hinbekommen  -  wäre nett, wenn du mir zeist wie das geht.
Sowas hier funktioniert nicht (Exception):


```
cameraGroup = simpleU.getViewingPlatform().getViewPlatformTransform();
camera = new Transform3D();
camera.setRotation(new AxisAngle4d(-0.07, 0.1, 0.0, 0.12));
camera.setTranslation(new Vector3f(0.3f, 0.3f, 8f));  
cameraGroup.setTransform( camera );

BranchGroup group = new BranchGroup();
group.addChild(cameraGroup);
```

Dein Link hat nicht wirklich geholfen. Ein Objekt per tastendruck um seine eigene Achse rotieren zu lassen habe ich schon hinbekommen, ich möchte aber die kamera um ein Objekt rotieren sehen.

Was ist ein KSKB?
Genauere Frage: Wie lasse ich den Viewpoint via tastendruck um ein beliebies Objekt rotieren? (Kein Problem, sondern wünschenswert, wenn das Objekt dabei selbst um seine eigene Achse rotiert)


----------



## bob_sheknowdas (3. Apr 2012)

Also...

Das mit dem Schachteln habe ich nicht hinbekommen  -  wäre nett, wenn du mir zeist wie das geht.
Sowas hier funktioniert nicht (Exception):


```
cameraGroup = simpleU.getViewingPlatform().getViewPlatformTransform();
camera = new Transform3D();
camera.setRotation(new AxisAngle4d(-0.07, 0.1, 0.0, 0.12));
camera.setTranslation(new Vector3f(0.3f, 0.3f, 8f));  
cameraGroup.setTransform( camera );

BranchGroup group = new BranchGroup();
group.addChild(cameraGroup);
```

Dein Link hat nicht wirklich geholfen. Ein Objekt per tastendruck um seine eigene Achse rotieren zu lassen habe ich schon hinbekommen, ich möchte aber die kamera um ein Objekt rotieren sehen.

Was ist ein KSKB?
Zur genaueren Frage: Wie lasse ich den Viewpoint via tastendruck um ein beliebies Objekt rotieren? (Kein Problem, sondern wünschenswert, wenn das Objekt dabei selbst um seine eigene Achse rotiert)


----------



## bob_sheknowdas (3. Apr 2012)

Update:

Habs geschaft ein beliebiges Objekt um meine Sphere rotieren zu lassen.
Dazu muss man einfach die Sphere in eine TransformGroup packen (Tf1), dann ein weiteres Objekt in eine neue TransformGroup stecken (Tf2) und etwas von der Sphere entfernt platzieren. Dann Tf1.addChild(Tf2) und anschließend Tf1 rotieren lassen.

Genau so möchte ich das auch mit meiner Kamera haben, aber mir gelingt es nicht das was bei 
simpleUniverse.getViewingPlatform().getViewPlatformTransform()  zurückkommt einer anderen Transformgroup als Child zuzufügen...


----------



## Marco13 (3. Apr 2012)

Ja, die Transform hängt ja auch schon im Szenegraph. Kannst du die ViewPlatformTransform nicht wie gewünscht ändern? 

KSKB = Kleines Selbstständig Kompilierbares Beispiel


----------



## bob_sheknowdas (3. Apr 2012)

Hmm, dazu sollte ich wohl sagen, dass ich gestern nachmittag das erste mal überhaupt irgendetwas mit j3d zu tun gehabt habe  

Was also ist der Szenegraph und wie könnte man die ViewPlatformTransform denn abändern, so das das Problem nicht mehr auftaucht?

Hier das gewünschte KSKB (wobei das erste K fast ein G sein müsste, aber sonst ist es nicht kompilierbar^^)
Steuerung via wasd, wie man sieht dreht sich nur die Sphere, die Kamera bleibt stehen. Beschleunigen + Bremsen(Rückwärtsfahren) mit Pfeiltasten (hier bewegt sich die Kamera mit)


```
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JFrame;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.SimpleUniverse;

public class test  extends JFrame implements Runnable, KeyListener{
	
	private Transform3D trans = new Transform3D();
	private TransformGroup mainTfGroup = new TransformGroup();
	private Transform3D camera;
	private TransformGroup cameraGroup;
	private GUI gui;
	private Float zloc = 0.0f;
	private float angley;
	private float anglex;
	private boolean right;
	private boolean left;
	private boolean up;
	private boolean down;
	private boolean speedup;
	private boolean slowdown;
	public float speed;
	
	public test() {
		setFocusable(true);
		setSize(300, 300);
		setPreferredSize(getSize());
		setLayout(new BorderLayout());
		
		create3D();
		
		addKeyListener(this);
		
		setVisible(true);
		
		Thread t = new Thread(this);
		t.start();
	}

	private void create3D() {
		Canvas3D c3d = new Canvas3D(SimpleUniverse.getPreferredConfiguration()); 
		SimpleUniverse simpleU = new SimpleUniverse(c3d);
		
		BranchGroup group = new BranchGroup();		
		
		TransformGroup tg = new TransformGroup();
		Transform3D t = new Transform3D();
		t.setTranslation(new Vector3f(2f,-2f , 0.0f));
		tg.setTransform(t);
		tg.addChild(new ColorCube(0.5f));
		group.addChild(tg);

		cameraGroup = simpleU.getViewingPlatform().getViewPlatformTransform();
		camera = new Transform3D();
		camera.setTranslation(new Vector3f(0.0f, 0.0f, 8f));  
		cameraGroup.setTransform( camera );
		
		Sphere sphere = new Sphere(0.5f);
		mainTfGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		trans.setTranslation(new Vector3f(0f,0f,0.0f));
		mainTfGroup.setTransform(trans);
		mainTfGroup.addChild(sphere);
		group.addChild(mainTfGroup);
		
		Color3f light1Color = new Color3f(1.8f, 0.1f, 0.1f);
		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
		Vector3f light1Direction = new Vector3f(1.0f, -3.0f, -12.0f);
		DirectionalLight light1 = new DirectionalLight(light1Color, light1Direction);
		light1.setInfluencingBounds(bounds);
		group.addChild(light1);
			
		simpleU.addBranchGraph(group);
		c3d.setFocusable(false);
		
		add(c3d);
	}

	@Override
	public void run() {
		while(true){
			if(right){
				angley -= 0.02;
			    trans.rotY(angley);
			}
			if(left){
				angley += 0.02;
			    trans.rotY(angley);
			}
			if(up){
				anglex += 0.01;
			    trans.rotX(anglex);
			}
			if(down){
				anglex -= 0.01;
			    trans.rotX(anglex);
			}
			if(speedup&&speed>=-0.05)speed-=0.0015;
			if(slowdown&&speed<=0.01)speed+=0.0015;
			zloc+=speed;
			trans.setTranslation(new Vector3f(0.0f,0.0f,zloc));
			camera.setTranslation(new Vector3f(0f, 0f, zloc+8f)); 
			mainTfGroup.setTransform(trans);
			cameraGroup.setTransform( camera );
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}	
		}
	}

        @Override
	public void keyTyped(KeyEvent e) {
		
	}

	@Override
	public void keyPressed(KeyEvent e) {
		switch(e.getKeyCode()){
		case 68: right=true; left=false; break;
		case 83: down=true; up=false; break;
		case 65: left=true; right=false; break;
		case 87: up=true; down=false; break;
		case 38: speedup=true; slowdown=false; break;
		case 40: slowdown=true; speedup=false; break;
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		switch(e.getKeyCode()){
		case 68: right=false; break;
		case 83: down=false; break;
		case 65: left=false; break;
		case 87: up=false; break;
		case 38: speedup=false; break;
		case 40: slowdown=false; break;
		}
	}
	public static void main(String[] args) {
		new test();

	}
}
```


----------



## Marco13 (3. Apr 2012)

Sowas wie

```
System.out.println("Set\n"+camera);
            cameraGroup.setTransform( camera );
```
hätte schon geholfen: Dort wird immer eine reine Translation gesetzt, und keine Rotation. Etwas mehr Bewegung sehen würde man mit 

```
camera.set(trans); // <------------------------------ Dieser Zeile...
            camera.setTranslation(new Vector3f(0f, 0f, zloc+8f));
```
um für die Kamera die gleiche Rotation zu verwenden wie für den Rest. Aber wie du sehen wirst, ist das vermutlich nicht genau das, was du willst  Man muss sich genau überlegen, wie die Steuerung aussehen soll, und wie man das erreicht - speziell in bezug auf die Frage, welche Informationen man speichern und wie verändern muss, und damit zusammenhängend, welche Reihenfolge oder Kombination man für Rotationen und Translationen verwenden muss. 

Vielleicht kannst du bei http://www.java-forum.org/spiele-multimedia-programmierung/122050-java-3d-steuerung-kskb.html ein bißchen was klauen, da hatte ich auch schonmal was dazu geschrieben. (BTW: Die Implementierung dort als "Behavior" hat Vorteile, kann aber im ersten Moment erschlagend wirken - relevant ist hauptsächlich der Teil nach [c]WASD UND PFEILTASTEN[/c] und wie dort auf den Matrizen rumgerechnet wird)


----------



## bob_sheknowdas (4. Apr 2012)

Also ich habs jetzt etwas anders gelöst.
Es ist möglich über

```
simpleU.getViewingPlatform().detach();
```
die Viewingplatform von ihrem Parent zu lösen und dann mittels

```
mainTfGroup.addChild(simpleU.getViewingPlatform());
```
an meine TransformGroup zu hängen. Dann rotiert die Kamera (um eine Achse) wie gewünscht.
Das problem ist die Rotation um 2 Achsen abwechselnd (wie ich jetzt festgestellt habe existiert das Problem von Anfang an).
Und zwar dreht er sich um die x-Achse wie erwartet, wenn ich dann um y rotieren willl spirngt er erst zur Ausgangsposition zurück und rotiert dann. Wenn ich dann wieder auf x-Rotation wechsel springt er auf die Position, bei der ich die letzte x-Rotation gestoppt habe und dreht sich dann weiter. Selbiges passiert beim Wechsel zur y-Rotation.

Hat zu dem Problem vllt wer einen guten Lösungsvorschlag?


----------



## Marco13 (4. Apr 2012)

Hattest du dir das verlinkte mal angesehen? 

Allgemein wirst du die Rotation und Translation vermutlich _getrennt_ als Matrix4f'e speichern müssen, und sie bei Tastendrücken verändern, und nur bei so einer Änderung die neue, gesamte Kamera-Matrix ausrechnen und auf den Knoten und die ViewPlatform anwenden. 

Das mit dem detach()... Klingt erstmal heikel, ich weiß nicht, ob das so eine gute Idee ist: Vielleicht wird sich intern darauf verlassen, die ViewPlatform an einer bestimmten Stelle zu finden, und durch das detachen _könnten_ Fehler entstehen, aber das ist nur unfundierte Spekulation...


----------



## bob_sheknowdas (5. Apr 2012)

hmm, also das was du da verlinkt hast ist im Prinzip das was ich haben will...

Allerdings zeigt mir Eclipse lauter Errors an, wenn ich den Code bei mir kompiliere. 
Und wirklich durchsteigen, geschweige denn das für mein Projekt anpassen bekomm ich mit meinen Kenntnissen auch nicht hin :bahnhof:


----------



## Marco13 (5. Apr 2012)

Du musst spezifischer sagen, was du vorhast, und worin die Fragen bestehen. 

```
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashSet;
import java.util.Set;

import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Group;
import javax.media.j3d.Light;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JFrame;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.SimpleUniverse;


class Player
{
    private final BranchGroup root;
    
    private final TransformGroup cameraTG;
    private final Transform3D cameraTransform;
    
    private final TransformGroup playerObjectTG;
    private final Transform3D playerObjectTransform;
    
    public Player(TransformGroup cameraTG)
    {
        this.root = new BranchGroup();
        
        this.cameraTG = cameraTG;
        this.cameraTransform = new Transform3D();
        
        this.playerObjectTG = new TransformGroup();
        playerObjectTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        this.playerObjectTransform = new Transform3D();
        
        playerObjectTG.addChild(new ColorCube(0.5f));
        
        root.addChild(playerObjectTG);
    }
    
    public void setTransform(Matrix4d transform)
    {
        playerObjectTransform.set(transform);
        playerObjectTG.setTransform(playerObjectTransform);
        
        cameraTransform.setIdentity();
        cameraTransform.setTranslation(new Vector3f(0.0f, 2.0f, 8.1f));
        cameraTransform.mul(playerObjectTransform, cameraTransform);
        cameraTG.setTransform(cameraTransform);
    }
    
    public Group getRoot()
    {
        return root;
    }
}


class PlayerControl implements KeyListener
{
    private final Player player;
    
    private final int forwardKey = KeyEvent.VK_W;
    private final int backwardKey = KeyEvent.VK_S;
    private final int leftKey = KeyEvent.VK_A;
    private final int rightKey = KeyEvent.VK_D;
 
    private double turnRightSpeed = -0.01;
    private double turnLeftSpeed = 0.01;
    private float moveForwardSpeed = -0.05f;
    private float moveBackwardSpeed = 0.05f;
    
    private final Set<Integer> pressedKeys = new HashSet<Integer>();
    
    private Matrix4d tempMatrix = new Matrix4d();
    private Matrix4d currentRotation = new Matrix4d();
    private Vector3d tempVector = new Vector3d();
    private Vector3d currentTranslation = new Vector3d();
    
    public PlayerControl(Player player)
    {
        this.player = player;
        currentRotation.setIdentity();

        Thread t = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                while (true)
                {
                    updateMovement();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }   
                }
            }
        });
        t.setDaemon(true);
        t.start();
    }
    
    private void updateMovement()
    {
        if (pressedKeys.contains(forwardKey))
        {
            moveForward();
        }
        else if (pressedKeys.contains(backwardKey))
        {
            moveBackward();
        }
        else if (pressedKeys.contains(leftKey))
        {
            turnLeft();
        }
        else if (pressedKeys.contains(rightKey))
        {
            turnRight();
        }
    }
 
    private void moveForward()
    {
        tempVector.set(0.0f,0.0f,moveForwardSpeed);
        currentRotation.transform(tempVector);
        currentTranslation.add(tempVector);
        updateAll();
    }
 
    private void moveBackward()
    {
        tempVector.set(0.0f,0.0f,moveBackwardSpeed);
        currentRotation.transform(tempVector);
        currentTranslation.add(tempVector);
        updateAll();
    }
 
    private void turnLeft()
    {
        tempMatrix.setIdentity();
        tempMatrix.rotY(turnLeftSpeed);
        currentRotation.mul(tempMatrix);
        updateAll();
    }
 
    private void turnRight()
    {
        tempMatrix.setIdentity();
        tempMatrix.rotY(turnRightSpeed);
        currentRotation.mul(tempMatrix);
        updateAll();
    }
 
    private void updateAll()
    {
        tempMatrix.setIdentity();
        tempMatrix.setTranslation(currentTranslation);
        tempMatrix.mul(currentRotation);
        player.setTransform(tempMatrix);
    }
 
    @Override
    public void keyTyped(KeyEvent e) 
    {
    }

    @Override
    public void keyPressed(KeyEvent e) 
    {
        pressedKeys.add(e.getKeyCode());
    }

    @Override
    public void keyReleased(KeyEvent e) 
    {
        pressedKeys.remove(e.getKeyCode());
    }
    
}


 
public class ThirdPerson extends JFrame 
{
    public ThirdPerson() 
    {
        setFocusable(true);
        setSize(600,600);
        setPreferredSize(getSize());
        setLayout(new BorderLayout());
        
        create3D();
        
        setVisible(true);
        
    }
    
    private void create3D() {
        Canvas3D canvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration()); 
        SimpleUniverse su = new SimpleUniverse(canvas3D);

        BranchGroup root = new BranchGroup();      
        TransformGroup rootTG = new TransformGroup();
        root.addChild(rootTG);
        
        rootTG.addChild(new Sphere(0.5f));
        
        TransformGroup cameraTG = su.getViewingPlatform().getViewPlatformTransform();
        
        Player player = new Player(cameraTG);
        rootTG.addChild(player.getRoot());
        rootTG.addChild(createLight());
        
        addKeyListener(new PlayerControl(player));

        su.addBranchGraph(root);
        canvas3D.setFocusable(false);
        add(canvas3D);
    }
    
    private static Light createLight()
    {
        Color3f lightColor = new Color3f(1.8f, 0.1f, 0.1f);
        BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
        Vector3f lightDirection = new Vector3f(1.0f, -3.0f, -12.0f);
        DirectionalLight light = new DirectionalLight(lightColor, lightDirection);
        light.setInfluencingBounds(bounds);
        return light;
    }
 
 
    public static void main(String[] args) {
        new ThirdPerson();
 
    }
}
```


(Und jetzt bitte keine Antwort: "Ja, fast, aber es sollte so-und-so sein, mit der Beschleunigung und so..."...)

Es hat übrirgens Vorteile, das mit Behaviors und nicht mit einem KeyListener und diesem IMHO fragwürdigen Animationsthread zu lösen... aber ... erfordert mehr Einarbeitungszeit...


----------

