Strange camera rotation

Started by Firzen, April 29, 2015, 05:39:22 PM

Previous topic - Next topic

Firzen

Hello,

I have problem with camera oriantation during camera rotation. The description of problem is here: http://stackoverflow.com/questions/29947901/jpct-strange-camera-rotation
Is there someone who can help me with that?

Thanks in advance!

EgonOlsen

I've answered in that Stackover thread of yours.

Firzen

Hi, I am finally returning back to work on this project..
I will continue here instead of StackOverflow because it will be longer.

I have tried to fix my problem with code you adviced to me but jPCT still does the same... Surely I could rotate the cube instead of camera but I have tried that before and there was problem with Rubik's cube moves. These rotations are not so easy and because of that I want to rotate camera instead of rotating objects. IMHO there should be no problem.

Frontend of my project currently contains only 3 important files. In Main.java you can see part of code where I added the code you have adviced to me.

Thanks in advance!

Main.java
package main;

import java.awt.Color;
import java.util.List;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

import cube.Move;
import cube.enums.BasicMove;
import cube.geom.Point3D;
import exceptions.FilterCharsError;
import exceptions.ParseException;
import exceptions.PointException;

public class Main {
private World world;
private FrameBuffer buffer;
private Cube3D cube;

private SimpleVector cameraPos = new SimpleVector(-20, 0, 0);
private SimpleVector cubeCenter = new SimpleVector(2, 2, 2);
private SimpleVector cameraUpVector = new SimpleVector(0, 1, 0);

public void printCube() throws ParseException, FilterCharsError, PointException {
Config.lightMul = 2;
initWorld();

cube.setRotationPivot(new Point3D(2, 2, 2));

world.getCamera().setPosition(cameraPos);
world.getCamera().lookAt(cubeCenter);

SimpleVector upVector = world.getCamera().getUpVector();
world.getCamera().setOrientation(world.getCamera().getDirection(), upVector);

cube.doMove(new Move(BasicMove.UP));

startPaintLoop();
}

private void startPaintLoop() throws PointException {

while (!org.lwjgl.opengl.Display.isCloseRequested()) {
refreshScene();

// *****************************************************************
// HERE I ADDED CODE THAT SHOULD SET FIXED CAMERA ORIENTATION
// *****************************************************************
world.getCamera().moveCamera(Camera.CAMERA_MOVEIN, cameraPos.distance(new SimpleVector(2, 2, 2)));
cameraPos.rotateAxis(new SimpleVector(0, 0, 1), (float) Math.toRadians(1));
world.getCamera().moveCamera(Camera.CAMERA_MOVEOUT, cameraPos.distance(new SimpleVector(2, 2, 2)));
world.getCamera().setPosition(cameraPos);
world.getCamera().lookAt(cubeCenter);

try {
Thread.sleep(50);
} catch (InterruptedException e) {

}
}

buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
buffer.dispose();
System.exit(0);
}

private void initWorld() {
world = new World();
world.setAmbientLight(20, 20, 20);
world.addLight(new SimpleVector(-15, -10, -14), new Color(200, 200, 200));
world.addLight(new SimpleVector(0, -30, 0), new Color(200, 200, 200));

world.getCamera().setPosition(cameraPos);

buffer = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_NORMAL);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);

world.getCamera().lookAt(cubeCenter);
cube = new Cube3D(3, world, buffer);
List<Field3D> fields = cube.getFields();

for(Object3D c : fields) {
world.addObject(c);
}
}

private void refreshScene() {
buffer.clear(java.awt.Color.BLUE);
world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
}

public static void main(String[] args) throws ParseException,
FilterCharsError, PointException {
Main m = new Main();
m.printCube();
}
}


Cube3D.java
package main;

import java.util.ArrayList;
import java.util.List;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureInfo;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;

import cube.Move;
import cube.enums.BasicMove;
import cube.enums.SideType;
import cube.geom.Point3D;
import exceptions.FilterCharsError;
import exceptions.ParseException;
import exceptions.PointException;

public class Cube3D {
private int size;
private List<Field3D> fields = new ArrayList<Field3D>();
private FrameBuffer buffer;
private World world;

private TextureInfo red = new TextureInfo(TextureManager.getInstance().getTextureID("red"));
private TextureInfo blue = new TextureInfo(TextureManager.getInstance().getTextureID("blue"));
private TextureInfo green = new TextureInfo(TextureManager.getInstance().getTextureID("green"));
private TextureInfo yellow = new TextureInfo(TextureManager.getInstance().getTextureID("yellow"));
private TextureInfo orange = new TextureInfo(TextureManager.getInstance().getTextureID("orange"));
private TextureInfo white = new TextureInfo(TextureManager.getInstance().getTextureID("white"));

public Cube3D(int size, World world, FrameBuffer buffer) {
setWorld(world);
setBuffer(buffer);
loadTextures();
setSize(size);
}

private void loadTextures() {
TextureManager.getInstance().addTexture("red",
new Texture("/home/firzen/develop/bakalarka/CubeGui/res/red.jpg"));
TextureManager.getInstance().addTexture("blue",
new Texture("/home/firzen/develop/bakalarka/CubeGui/res/blue.jpg"));
TextureManager.getInstance().addTexture("green",
new Texture("/home/firzen/develop/bakalarka/CubeGui/res/green.jpg"));
TextureManager.getInstance().addTexture("yellow",
new Texture("/home/firzen/develop/bakalarka/CubeGui/res/yellow.jpg"));
TextureManager.getInstance().addTexture("orange",
new Texture("/home/firzen/develop/bakalarka/CubeGui/res/orange.jpg"));
TextureManager.getInstance().addTexture("white",
new Texture("/home/firzen/develop/bakalarka/CubeGui/res/white.jpg"));

red = new TextureInfo(TextureManager.getInstance().getTextureID("red"));
blue = new TextureInfo(TextureManager.getInstance().getTextureID("blue"));
green = new TextureInfo(TextureManager.getInstance().getTextureID("green"));
yellow = new TextureInfo(TextureManager.getInstance().getTextureID("yellow"));
orange = new TextureInfo(TextureManager.getInstance().getTextureID("orange"));
white = new TextureInfo(TextureManager.getInstance().getTextureID("white"));
}

private Field3D createField(SimpleVector move) {
Field3D box = new Field3D(1000, world, buffer);

SimpleVector upperLeftFront = new SimpleVector(-1 + move.x,-1 + move.y,-1 + move.z);
SimpleVector upperRightFront = new SimpleVector(1 + move.x,-1 + move.y,-1 + move.z);
SimpleVector lowerLeftFront = new SimpleVector(-1 + move.x,1 + move.y,-1 + move.z);
SimpleVector lowerRightFront = new SimpleVector(1 + move.x,1 + move.y,-1 + move.z);

SimpleVector upperLeftBack = new SimpleVector(-1 + move.x, -1 + move.y, 1 + move.z);
SimpleVector upperRightBack = new SimpleVector(1 + move.x, -1 + move.y, 1 + move.z);
SimpleVector lowerLeftBack = new SimpleVector(-1 + move.x, 1 + move.y, 1 + move.z);
SimpleVector lowerRightBack = new SimpleVector(1 + move.x, 1 + move.y, 1 + move.z);

// Front
box.addTriangle(upperLeftFront, lowerLeftFront, upperRightFront, yellow);
box.addTriangle(upperRightFront, lowerLeftFront, lowerRightFront, yellow);

// Back
box.addTriangle(upperLeftBack, upperRightBack, lowerLeftBack, white);
box.addTriangle(upperRightBack, lowerRightBack, lowerLeftBack, white);

// Upper
box.addTriangle(upperLeftBack, upperLeftFront, upperRightBack, red);
box.addTriangle(upperRightBack, upperLeftFront, upperRightFront, red);

// Lower
box.addTriangle(lowerLeftBack, lowerRightBack, lowerLeftFront, orange);
box.addTriangle(lowerRightBack, lowerRightFront, lowerLeftFront, orange);

// Left
box.addTriangle(upperLeftFront, upperLeftBack, lowerLeftFront, green);
box.addTriangle(upperLeftBack, lowerLeftBack, lowerLeftFront, green);

// Right
box.addTriangle(upperRightFront, lowerRightFront, upperRightBack, blue);
box.addTriangle(upperRightBack, lowerRightFront, lowerRightBack, blue);

box.build();
return box;
}

private void initCube(int size) {
fields.clear();

for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
for(int k = 0; k < size; k++) {
Field3D field = createField(new SimpleVector(i * 2, j * 2, k * 2));
field.setLocation(new Point3D(i, j, k));
fields.add(field);
}
}
}
}

public void doMoves(String str) throws PointException, ParseException,
FilterCharsError {
List<Move> moves = Move.parseMoves(str);
for(Move m : moves) {
System.out.println(m);
doMove(m);
}
}

// XXX temporary
public void doMove(Move move) throws PointException {
doMove(move, false);
}

public void doMove(Move move, boolean test) throws PointException {
List<Field3D> fields = getFieldsAtLevel(move);

Point3D sum = new Point3D();
for(Field3D field : fields) {
sum = Point3D.plus(sum, field.getLocation());
}

sum = Point3D.divide(sum, fields.size());

System.out.println("Rotation center: " + sum);

setFieldsInnerCenter(fields, sum);

boolean rotate = true;
while(rotate) {
for(Field3D field : fields) {
rotate = field.rotateStep(move);
}

refreshScene();

try {
Thread.sleep(200);
} catch (InterruptedException e) {

}
}

for(Field3D field : fields) {
field.resetRotations();
field.setLastAxis(getRotationAxisFromMove(move));
}
}

private void setFieldsInnerCenter(List<Field3D> fields, Point3D pivot) {
for(Field3D field : fields) {
field.setInnerCenter(pivot);
}
}

private RotationAxis getRotationAxisFromMove(Move move) {
BasicMove bm = move.getBasicMove();
switch(bm) {
case BACK: return RotationAxis.Y;
case BACK_INVERTED: return RotationAxis.Y;
case DOWN: return RotationAxis.Z;
case DOWN_INVERTED: return RotationAxis.Z;
case FRONT: return RotationAxis.Y;
case FRONT_INVERTED: return RotationAxis.Y;
case LEFT: return RotationAxis.X;
case LEFT_INVERTED: return RotationAxis.X;
case RIGHT: return RotationAxis.X;
case RIGHT_INVERTED: return RotationAxis.X;
case UP: return RotationAxis.Z;
case UP_INVERTED: return RotationAxis.Z;
}

return null;
}

// XXX temp
private void printFields(List<Field3D> fields) {
for(Field3D field : fields) {
System.out.println(field.getLocation());
}
}

private void refreshScene() {
buffer.clear(java.awt.Color.BLUE);
world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
}

public void rotateX(double angle) {
for(Field3D field : fields) {
field.rotateWholeX((float) Math.toRadians(angle));
}
refreshScene();
}

public void rotateY(double angle) {
for(Field3D field : fields) {
field.rotateWholeY((float) Math.toRadians(angle));
}
refreshScene();
}

public void rotateZ(double angle) {
for(Field3D field : fields) {
field.rotateWholeZ((float) Math.toRadians(angle));
}
refreshScene();
}

public Field3D getField(int x, int y, int z) {
return getField(new Point3D(x, y, z));
}

public Field3D getField(Point3D location) {
for(Field3D field : fields) {
if(field.getLocation().equals(location)) {
return field;
}
}
return null;
}

public List<Field3D> getFieldsAtLevel(Move move) {
List<Field3D> output = new ArrayList<Field3D>();
SideType side = SideType.parseBasicMove(move.getBasicMove());
int level = move.getLevel();
if(side == SideType.DOWN || side == SideType.RIGHT
|| side == SideType.FRONT) {
level = size - level - 1;
}

if(side == SideType.UP || side == SideType.DOWN) {
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
Field3D f = getField(x, y, level);
if(f != null) {
output.add(f);
}
}
}
}
else if(side == SideType.LEFT || side == SideType.RIGHT) {
for(int z = 0; z < size; z++) {
for(int y = 0; y < size; y++) {
Field3D f = getField(level, y, z);
if(f != null) {
output.add(f);
}
}
}
}
else if(side == SideType.FRONT || side == SideType.BACK) {
for(int z = 0; z < size; z++) {
for(int x = 0; x < size; x++) {
Field3D f = getField(x, level, z);
if(f != null) {
output.add(f);
}
}
}
}

return output;
}

public void setRotationPivot(Point3D center) {
setRotationPivot(point3DtoSimpleVector(center));
}

public void setRotationPivot(SimpleVector vector) {
for(Field3D field : fields) {
field.setRotationPivot(vector);
}
}

private SimpleVector point3DtoSimpleVector(Point3D pt) {
return new SimpleVector(pt.getX(), pt.getY(), pt.getZ());
}

public List<Field3D> getFields() {
return fields;
}

public void setFields(List<Field3D> fields) {
this.fields = fields;
}

public int getSize() {
return size;
}

public void setSize(int size) {
this.size = size;
initCube(size);
}

public FrameBuffer getBuffer() {
return buffer;
}

public void setBuffer(FrameBuffer buffer) {
this.buffer = buffer;
}

public World getWorld() {
return world;
}

public void setWorld(World world) {
this.world = world;
}
}


Field3D.java
package main;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

import cube.AbstractField;
import cube.Move;
import cube.enums.BasicMove;
import cube.geom.Point3D;
import exceptions.PointException;

public class Field3D extends Object3D implements AbstractField {
private static final long serialVersionUID = 1L;

private Point3D location;
private FrameBuffer buffer;
private World world;
private RotationAxis lastAxis = null;
private Point3D innerCenter = new Point3D();

private int rotationsRemaining = -1;

public Field3D(int arg0, World world, FrameBuffer buffer) {
super(arg0);
setWorld(world);
setBuffer(buffer);
}

@Override
public Point3D getLocation() {
return location;
}

@Override
public void setLocation(Point3D location) {
this.location = location;
}


public boolean rotateStep(Move move) throws PointException {

return rotateStep(move.getBasicMove());
}

private boolean rotateStep(BasicMove move) throws PointException {
final int parts = 7;

if(rotationsRemaining == -1) {
rotationsRemaining = parts;
}

final float partialAngle = getTotalAngle(move) / (float) parts;

if(rotationsRemaining > 0) {
rotate(move, partialAngle);
rotationsRemaining--;
return true;
}
else {
Point3D before = location.getClone();
float angle = getTotalAngle(move);

if(!BasicMove.FRONT.equals(move) && !BasicMove.BACK.equals(move)) {
angle = -angle;
}

location.rotate(move, innerCenter, angle);
System.out.println(before + " vs " + location + "; angle: " + getTotalAngle(move));
return false;
}
}

public void rotate(BasicMove move, float angle) throws PointException {
switch(move) {
case LEFT: rotateX((float) Math.toRadians(angle));
break;
case RIGHT: rotateX((float) Math.toRadians(angle));
break;
case UP: rotateZ((float) Math.toRadians(angle));
break;
case DOWN: rotateZ((float) Math.toRadians(angle));
break;
case FRONT: rotateY((float) Math.toRadians(angle));
break;
case BACK: rotateY((float) Math.toRadians(angle));
break;

case LEFT_INVERTED: rotate(BasicMove.RIGHT, angle); break;
case RIGHT_INVERTED: rotate(BasicMove.LEFT, angle); break;
case UP_INVERTED: rotate(BasicMove.DOWN, angle); break;
case DOWN_INVERTED: rotate(BasicMove.UP, angle); break;
case FRONT_INVERTED: rotate(BasicMove.BACK, angle); break;
case BACK_INVERTED: rotate(BasicMove.FRONT, angle); break;
}
}

/**
* Vrátí úhel ve stupních, o který je třeba rotovat dílek při daném tahu
* na kostce.
* @param move BasicMove
* @return float
*/
private float getTotalAngle(BasicMove move) {
switch(move) {
case LEFT: return -90f;
case RIGHT: return 90f;
case UP: return -90f;
case DOWN: return 90f;
case FRONT: return 90f;
case BACK: return -90f;

case LEFT_INVERTED: return getTotalAngle(BasicMove.RIGHT);
case RIGHT_INVERTED: return getTotalAngle(BasicMove.LEFT);
case UP_INVERTED: return getTotalAngle(BasicMove.DOWN);
case DOWN_INVERTED: return getTotalAngle(BasicMove.UP);
case FRONT_INVERTED: return getTotalAngle(BasicMove.BACK);
case BACK_INVERTED: return getTotalAngle(BasicMove.FRONT);

default: return 0f;
}
}

private Matrix getRotationMatrixX(float angle) {
Matrix m = new Matrix();
m.setRow(0, 1, 0, 0, 0);
m.setRow(1, 0, (float) Math.cos(angle), (float) -Math.sin(angle), 0);
m.setRow(2, 0, (float) Math.sin(angle), (float) Math.cos(angle), 0);
m.setRow(3, 0, 0, 0, 1);
return m;
}

private Matrix getRotationMatrixY(float angle) {
Matrix m = new Matrix();
m.setRow(0, (float) Math.cos(angle), 0, (float) Math.sin(angle), 0);
m.setRow(1, 0, 1, 0, 0);
m.setRow(2, (float) -Math.sin(angle), 0, (float) Math.cos(angle), 0);
m.setRow(3, 0, 0, 0, 1);
return m;
}

private Matrix getRotationMatrixZ(float angle) {
Matrix m = new Matrix();
m.setRow(0, (float) Math.cos(angle), (float) -Math.sin(angle), 0, 0);
m.setRow(1, (float) Math.sin(angle), (float) Math.cos(angle), 0, 0);
m.setRow(2, 0, 0, 1, 0);
m.setRow(3, 0, 0, 0, 1);
return m;
}

public void rotateWholeX(float angle) {
Matrix m = getRotationMatrix();
m.matMul(getRotationMatrixX(angle));
setRotationMatrix(m);
}

public void rotateWholeY(float angle) {
Matrix m = getRotationMatrix();
m.matMul(getRotationMatrixY(angle));
setRotationMatrix(m);
}

public void rotateWholeZ(float angle) {
Matrix m = getRotationMatrix();
m.matMul(getRotationMatrixZ(angle));
setRotationMatrix(m);
}

@Override
public void rotateX(float angle) {
rotateWholeX(angle);
}

@Override
public void rotateY(float angle) {
rotateWholeY(angle);
}

@Override
public void rotateZ(float angle) {
rotateWholeZ(angle);
}

// @Override
// public void rotateX(float angle) {
// System.out.println("rotate x, last " + lastAxis);
//
// if(lastAxis != null && lastAxis.equals(RotationAxis.Z)) {
// setLastAxis(null);
// rotateY(-angle);
// setLastAxis(RotationAxis.Z);
// }
// else {
// Matrix m = getRotationMatrix();
//
// SimpleVector axis = new SimpleVector(1, 0, 0);
// axis.rotate(m);
//
// rotateAxis(axis, angle);
// }
// }
//
// @Override
// public void rotateY(float angle) {
// System.out.println("rotate y");
//
// if(lastAxis != null && lastAxis.equals(RotationAxis.Z)) {
// rotateX(angle);
// }
// else {
// Matrix m = getRotationMatrix();
//
// SimpleVector axis = new SimpleVector(0, 1, 0);
// axis.rotate(m);
//
// rotateAxis(axis, angle);
// }
// }
//
// @Override
// public void rotateZ(float angle) {
// System.out.println("rotate z");
//
// Matrix m = getRotationMatrix();
//
// SimpleVector axis = new SimpleVector(0, 0, 1);
// axis.rotate(m);
//
// rotateAxis(axis, angle);
// }

public void rotate(Move move, float angle)
throws PointException {
rotate(move.getBasicMove(), angle);
}

public FrameBuffer getBuffer() {
return buffer;
}

public void setBuffer(FrameBuffer buffer) {
this.buffer = buffer;
}

public World getWorld() {
return world;
}

public void setWorld(World world) {
this.world = world;
}

public void resetRotations() {
rotationsRemaining = -1;
}

public RotationAxis getLastAxis() {
return lastAxis;
}

public void setLastAxis(RotationAxis lastAxis) {
this.lastAxis = lastAxis;
}

public Point3D getInnerCenter() {
return innerCenter;
}

public void setInnerCenter(Point3D innerCenter) {
this.innerCenter = innerCenter;
}
}

Firzen

I have finally got this work thanks to one of your examples, so you can ignore my previous post.
Thank you very much!

EgonOlsen

Cool! Glad you made it work. What did you do to solve it?

Firzen

Thanks! Currently I do not have much time for explaining the solution, but after finishing my school work I could post it here.