Move Camera around a scene

Started by tyronetudehope, February 25, 2011, 02:59:06 PM

Previous topic - Next topic

tyronetudehope

Hi, I have a scene, which basically just contains a textured sphere object.

What I would like to do, is move the camera around the sphere in a circular motion, would look as if the sphere itself was rotating. I've got the sphere to rotate, but as an experiment would like to figure out how to rotate the camera.

I've been reading on all sorts of calculations and translations to do this, but have yet to get it right.

Any help please?

raft

two approaches i can think of:

* keep track of an angle value, increase it every frame, calculate camera location around your sphere by using sin and cos angle, place camera accordingly and finally call Camera.lookAt(sphere)
* create two dummy objects, make second child of first, translate second to a distance (radius of your rotation), set parent object's location to sphere's location. each frame rotate parent object, set camera location to child objects location and finally call Camera.lookAt(sphere)

tyronetudehope

Thank you! I went with the dummy object approach and it worked perfectly!

Thanks!

phoenicoperus

#3
I'm also using the dummy approach but I have a strange behaviour.

when I rotate the pivot dummy all works until I get closer to the top (90ยบ) of the rotation, if I use camera.lookAt(), the camera should be upside down if you pass the top of the rotation, but it always stays upright, that causes a very nasty feel.

I also thought about using camera.setOrientation(dir,up) but I cant get it right.

The goal is to achieve a camara that moves around the object at a constant 3d radius with the camera Oriented to the centre of the object with "the exact same feel as the helloWorld AE" but rotating and moving the camera instead of rotating the 3dobject with a fixed camera

Here Is what I tried so far:
private void resetCameraPosition() {
camera.setPosition(cameraSatellite.getTransformedCenter());
camera.lookAt(cameraPivot.getTransformedCenter());
}

private void initCamera() {

               //move the dummy pivot to the centre of the 3dobject
cameraPivot = Object3D.createDummyObj();
cameraPivot.translate(calcTrans(cameraPivot, 3dobject));

               //move the dummy satellite to the centre of the 3dobject and separated it by a radius (-20)
cameraSatellite = Object3D.createDummyObj();
cameraSatellite.translate(calcTrans(cameraSatellite, 3dobject));
cameraSatellite.translate(new SimpleVector(0, 0, -20));

               //Made child
cameraPivot.addChild(cameraSatellite);

camera = world.getCamera();
resetCameraPosition();
}




To turn the cameraPivot dummy I'm using the same code as in the AE helloWorld
if (touchTurn != 0) {
cameraPivot.rotateY(touchTurn);
resetCameraPosition();
// camera.rotateCameraY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
cameraPivot.rotateX(touchTurnUp);
resetCameraPosition();
// camera.rotateCameraX(touchTurnUp);
touchTurnUp = 0;
}



I also tried several approaches for the resetCameraPosition() method, including getBack with invert, setOrientation etc etc. And none of them behaved as I would expect : (

Any idea of how to get this one right? Maby someone could do an example of the helloWorld using camera rotation instead of primitive cube rotation. It would be very handy.

EgonOlsen

To create a camera moving around an object like in HelloWorld, you can do something like i did here: http://www.jpct.net/forum2/index.php/topic,977.msg6207.html#msg6207. It's for desktop jPCT, but the basic idea stays the same. That code has two options: Rotate object or rotate camera (default). Maybe that will help.

phoenicoperus

#5
Thanks ill give it a try, although its going to be hard for me to change it to android :(, and I was really looking for the dummy implementation.

EgonOlsen

It's pretty simple to port. Just a few lines really matter:

m.rotateAxis(m.getXAxis(), (float) -Math.PI/2f);
cam.moveCamera(Camera.CAMERA_MOVEIN, distance);
cam.rotateAxis(m.invert3x3().getXAxis(), line.length() / 200f);
cam.moveCamera(Camera.CAMERA_MOVEOUT, distance);


The problem with the lookAt-approach is, that lookAt() only guarantees that the camera looks at a target. It doesn't make any assumptions about the rotation of the camera around the vector between the camera and the target. Or in other words: There are unlimited ways of looking at a target.

phoenicoperus

#7
Since there is unlimited ways of looking at something, I'm trying to set the camera relative to the camerapivot dummy
private void resetCameraPosition() {
camera.setPosition(cameraSatellite.getTransformedCenter());
camera.setBack(cameraPivot.getRotationMatrix().cloneMatrix().invert());
}


although it still doesn't feel right.

The problem I have implementing your method is that I'm not sure how to make it interact with touchTurn and touchTurnUp.

Ill upload the modified hello world if we can get this one right It will be very good example for the wiki which currently only has info about fps style camera

phoenicoperus

#8
Here is the HelloWorldCamera with the camera rotation. I'm trying to achieve the exact same feel as the normal HelloWorld but moving the camera instead of rotating the object. As you can see my problem is that I don't know how to code a lookAt method that matches the expected behaviour.

HelloWorldCamera:
http://www.mediafire.com/?8p4lcdqefzbhg3o

EgonOlsen

This is what i had in mind:

package com.threed.jpct.example;

import java.lang.reflect.Field;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.BitmapHelper;
import com.threed.jpct.util.MemoryHelper;

/**
* A simple demo. This shows more how to use jPCT-AE than it shows how to write
* a proper application for Android. It includes basic activity management to
* handle pause and resume...
*
* @author EgonOlsen
*
*/
public class HelloWorldCamera extends Activity {

// Used to handle pause and resume...
private static HelloWorldCamera master = null;

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D cube = null;
private int fps = 0;

private Camera camera = null;
private SimpleVector line = new SimpleVector();
private Matrix m = new Matrix();

private Light sun = null;

protected void onCreate(Bundle savedInstanceState) {

Logger.log("onCreate");

if (master != null) {
copy(master);
}

super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
// Ensure that we get a 16bit framebuffer. Otherwise, we'll fall
// back to Pixelflinger on some device (read: Samsung I7500)
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);

setTitle("HelloWorld with cam rotation");
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

protected void onStop() {
super.onStop();
}

private void copy(Object src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

public SimpleVector calcTrans(Object3D orgin, Object3D destination) {
// Vector trans = (B-A)
SimpleVector trans = new SimpleVector(destination.getTransformedCenter().x - orgin.getTransformedCenter().x, destination.getTransformedCenter().y
- orgin.getTransformedCenter().y, destination.getTransformedCenter().z - orgin.getTransformedCenter().z);
return trans;
}

protected boolean isFullscreenOpaque() {
return true;
}

class MyRenderer implements GLSurfaceView.Renderer {

private long time = System.currentTimeMillis();
private boolean stop = false;

public MyRenderer() {
}

public void stop() {
stop = true;
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
if (fb != null) {
fb.dispose();
}
fb = new FrameBuffer(gl, w, h);

if (master == null) {

world = new World();
world.setAmbientLight(20, 20, 20);

sun = new Light(world);
sun.setIntensity(250, 250, 250);

// Create a texture out of the icon...:-)
Texture texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(getResources().getDrawable(R.drawable.icon)), 64, 64));
TextureManager.getInstance().addTexture("texture", texture);

cube = Primitives.getCube(10);
// cube.calcTextureWrapSpherical();
// cube.setTexture("texture");
cube.strip();
cube.build();

world.addObject(cube);

camera = world.getCamera();
camera.moveCamera(Camera.CAMERA_MOVEOUT, 50);

SimpleVector sv = new SimpleVector();
sv.set(cube.getTransformedCenter());
sv.y -= 100;
sv.z -= 100;
sun.setPosition(sv);
MemoryHelper.compact();

if (master == null) {
Logger.log("Saving master Activity!");
master = HelloWorldCamera.this;
}
}
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

public void onDrawFrame(GL10 gl) {

try {
if (!stop) {

line.set(-touchTurn, 0, touchTurnUp);
m = line.normalize(line).getRotationMatrix(m);
m.rotateAxis(m.getXAxis(), (float) -Math.PI / 2f);
camera.moveCamera(Camera.CAMERA_MOVEIN, 50); // 50 is the distance from the cube's center that the camera has
camera.rotateCameraAxis(m.invert3x3().getXAxis(), -line.length() / 5f); // 5f actually depends on render speed
camera.moveCamera(Camera.CAMERA_MOVEOUT, 50);

fb.clear(back);
world.renderScene(fb);
world.draw(fb);
fb.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
} else {
if (fb != null) {
fb.dispose();
fb = null;
}
}
} catch (Exception e) {
Logger.log(e, Logger.MESSAGE);
}
}
}
}



phoenicoperus

#10
The movement of the camera is correct, that's the idea of how it should behave but its way too sensitive, I'm testing it in a nexus one.

I can see the comment "// 5f actually depends on render speed" so there should be a way of dynamically calculate it so it works the same in each device

EgonOlsen

Yes, make this value dependent on the frame rate or the time passed to render the last frame. The 5f is just something that worked fine in the emulator.

phoenicoperus

#12
Thank you so much, for your time and your help. In return Ill try to make a clear commented example for the the wiki and make a smoother method controlling leaving the finger on the screen and the 5f calc method. Once again thanks ^_^


EgonOlsen

If you need an account for the wiki, please let me know. I had to disable free registration due to massive spam.

phoenicoperus

#14
I solved the flickering when keeping the finger still on screen by adding a simple if statement
line.set(-touchTurn, 0, touchTurnUp);
m = line.normalize(line).getRotationMatrix(m);
m.rotateAxis(m.getXAxis(), (float) -Math.PI / 2f);
if((touchTurn > 0.01f || touchTurn < -0.01f) || (touchTurnUp > 0.01f || touchTurnUp < -0.01f))
{
camera.moveCamera(Camera.CAMERA_MOVEIN, 50); // 50 is the distance from the cube's center that the camera has
camera.rotateCameraAxis(m.invert3x3().getXAxis(), -line.length() / 15f); // 5f actually depends on render speed
camera.moveCamera(Camera.CAMERA_MOVEOUT, 50);
touchTurn = 0;
touchTurnUp = 0;
}


now I just have to do the method that calculates the rotate camera angle depending on the frames per second or time passed to render the last frame, although I'm not sure of how I'm going to achieve this ??? I'm trying with the fps approach but rotation suddenly accelerates : ( , I'll keep trying to get a reliable value for this (and constant) ; )