rotate around edge

Started by guillaume, March 26, 2012, 10:44:53 AM

Previous topic - Next topic

guillaume

I have a  thin board-like  object, when I
call  obj.rotateX(), the board will rotate around its upper edge,
now I want to make it rotate around its lower edge, what should
I do with jpct ? thanks.


guillaume


urubu

I have tried to understand how rotation of a cube  around a pivot works to no avail. What I am trying to achieve is to be able to rotate reliably a cube around any one of its edge. Here is what I am doing:

  • create a cube with corners (0,0,0) and (1,1,1)
  • translate the cube to (-1,-1,-1)
  • set the pivot to (0,1,0) and rotate the the cube around the Z axis Math.PI radians. The cube rotates left as expected (relative to where the camera is set)
  • set the pivot to (0,1,1) and rotate the cube around the X axis Math.PI radians)
I was expecting to have the cube to rotate around the lower back edge but it rotated around the top back edge.
I tried several combinations of pivot setting but nothing worked.

I have looked at a couple of examples and I thought I had understood how rotation around a pivot worked but I figured I was wrong...

Looking for some help

EgonOlsen

Can you create a test case? It should work and from your description alone, i'm not sure what should be wrong here. Here a quick example:


import com.threed.jpct.*;

public class HelloWorldOGLCube {
private World world;

private FrameBuffer buffer;

private Object3D box;

public static void main(String[] args) throws Exception {
new HelloWorldOGLCube().loop();
}

public HelloWorldOGLCube() throws Exception {
world = new World();
world.setAmbientLight(255, 255, 255);

TextureManager.getInstance().addTexture("box", new Texture("box.jpg"));

box = Primitives.getBox(10f, 1f);

box.setTexture("box");
box.rotateY((float)-Math.PI/4f);
box.rotateMesh();
box.clearRotation();
box.setEnvmapped(Object3D.ENVMAP_ENABLED);
box.build();
world.addObject(box);

box.compile();

world.getCamera().setPosition(0, 0, -80);
}

private void loop() throws Exception {
buffer = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_GL_AA_2X);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);

//box.setRotationPivot(new SimpleVector(0,10,10)); // lower edge
box.setRotationPivot(new SimpleVector(0,-10,10)); // upper edge

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

box.rotateX(0.01f);

buffer.clear(java.awt.Color.BLUE);
world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
Thread.sleep(10);
}
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
buffer.dispose();
System.exit(0);
}
}


urubu

Here is the code I am using. Rotate the top pink box to the left (using the up arrow key) until it is at the same height as the green bottom box (PI rotation on Z). Then (using the down arrow key)  rotate it to the back (X rotation) reproduces the behaviour I described.

package com.cubetest.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.KeyEvent;
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;

/**

*/
public class HelloWorld extends Activity {

// Used to handle pause and resume...
private static HelloWorld 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 RGBColor c1 = new RGBColor(50, 100, 50);
private RGBColor c2 = new RGBColor(100, 50, 50);
private Object3D cb=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);
}

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

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

@Override
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);
}
}



protected boolean isFullscreenOpaque() {
return true;
}


public boolean onKeyDown(int keyCode, KeyEvent event) {
SimpleVector sv;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
sv=new SimpleVector(0,1,0);
cb.setRotationPivot(sv);
cb.rotateZ((float)Math.PI/10);
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
sv=new SimpleVector(0,1,1);
cb.setRotationPivot(sv);
cb.rotateX((float)Math.PI/10);
return true;

}
return false;
}


class MyRenderer implements GLSurfaceView.Renderer {

public MyRenderer() {
}

public Object3D createBox() {

Object3D cube=new Object3D(12);

cube.addTriangle( new SimpleVector( 0, 0, 0 ),
0, 0,
new SimpleVector( 0, 1, 0 ), 0, 1,
new SimpleVector( 1, 1, 0 ), 1, 1);
cube.addTriangle( new SimpleVector( 1, 1, 0 ),
1, 1,
new SimpleVector( 1, 0, 0 ), 1, 0,
new SimpleVector( 0, 0, 0 ), 0, 0);

cube.addTriangle( new SimpleVector( 0, 0, 1 ),
0, 0,
new SimpleVector( 0, 1, 1 ), 0, 1,
new SimpleVector( 0, 1, 0 ), 1, 1);
cube.addTriangle( new SimpleVector( 0, 1, 0 ),
1, 1,
new SimpleVector( 0, 0, 0 ), 1, 0,
new SimpleVector( 0, 0, 1 ), 0, 0);
cube.addTriangle( new SimpleVector( 1, 0, 0 ),
0, 0,
new SimpleVector( 1, 1, 0 ), 0, 1,
new SimpleVector( 1, 1, 1 ), 1, 1);
cube.addTriangle( new SimpleVector( 1, 1, 1 ),
1, 1,
new SimpleVector( 1, 0, 1 ), 1, 0,
new SimpleVector( 1, 0, 0 ), 0, 0);
cube.addTriangle( new SimpleVector( 1, 0, 1 ),
0, 0,
new SimpleVector( 1, 1, 1 ), 0, 1,
new SimpleVector( 0, 1, 1 ), 1, 1);
cube.addTriangle( new SimpleVector( 0, 1, 1 ),
1, 1,
new SimpleVector( 0, 0, 1 ), 1, 0,
new SimpleVector( 1, 0, 1 ), 0, 0);
cube.addTriangle( new SimpleVector( 0, 0, 1 ),
0, 0,
new SimpleVector( 0, 0, 0 ), 0, 1,
new SimpleVector( 1, 0, 0 ), 1, 1);
cube.addTriangle( new SimpleVector( 1, 0, 0 ),
1, 1,
new SimpleVector( 1, 0, 1 ), 1, 0,
new SimpleVector( 0, 0, 1 ), 0, 0);
cube.addTriangle( new SimpleVector( 0, 1, 0 ),
0, 0,
new SimpleVector( 0, 1, 1 ), 0, 1,
new SimpleVector( 1, 1, 1 ), 1, 1 );
cube.addTriangle( new SimpleVector( 1, 1, 1 ),
1, 1,
new SimpleVector( 1, 1, 0 ), 1, 0,
new SimpleVector( 0, 1, 0 ), 0, 0 );

cube.setCenter(new SimpleVector(0,0,0));

return cube;
}



public void buildObj() {
if (master == null) {
world = new World();
cb=createBox();
cb.translate(-1, 0, -1);
cb.setAdditionalColor(c1);
cb.build();
world.addObject(cb);
cb=createBox();
cb.translate(-1, -1, -1);
cb.setAdditionalColor(c2);
cb.build();
world.addObject(cb);
world.getCamera().setPosition(-0.5f, -0.5f, -5);
world.getCamera().lookAt(new SimpleVector(-0.5,-0.5,-0.5));


}
}


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

}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {

}

public void onDrawFrame(GL10 gl) {

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

EgonOlsen

The rotation axes are in object space. Object space isn't affected by any rotations of the object, so if you rotate the cube in a way that it ends up below your rotation axis, what was formerly a rotation around it's lower edge will become a rotation around it's upper edge.

urubu

#7
Quote from: EgonOlsen on April 05, 2012, 08:58:45 PM
The rotation axes are in object space. Object space isn't affected by any rotations of the object, so if you rotate the cube in a way that it ends up below your rotation axis, what was formerly a rotation around it's lower edge will become a rotation around it's upper edge.

My understanding of your explanation (and that may be where I am misunderstanding the concept) is that if I specify a pivot that is an edge it will always rotate around that edge. It may be upside-down and give the impression it is rotating in a lower (or upper) edge but it would always rotate around the same edge in terms of local coordinate.

Using the test code I sent in the previous post I noticed that that was true for some edges but not for all of them. Using that test code, if I  set the pivot to (0,0,1) and rotateX the cube rotates around the upper back edge as expected.

On the other hand, when I execute the steps below, things do not work as I expected. Here are the steps and what happens:

  • Set the pivot to (0,1,0) and rotate 180 degrees around Z. This works as expected. The cube is rotated around the left lower edge and ends up upside-down
  • On top of the previous rotation, set the pivot now to (0,0,1) and rotate around X. I would expect the cube to be rotated around the same edge as before but what happens is that the cube "jumps" (I think it gets translated somehow) to (0,0,0) and rotates around the edge there.

Please notice that I am using jpct-ae and that all vectors are considering the cubes created using the test code I posted.

EgonOlsen

Rotations are cummulative, but the rotation pivot isn't. I.e. if you rotate 90° around z with a pivot of (1,0,0) and you then change the pivot to (-1,0,0), the result will be different. That's why you are getting this "jump". In your case, you might want to consider to use the rotateAxis()-method instead with the axis being the edge you want to rotate around. You have to calculate that edge based on your object's rotation matrix though.

urubu

Quote from: EgonOlsen on April 08, 2012, 09:14:46 PM
Rotations are cummulative, but the rotation pivot isn't. I.e. if you rotate 90° around z with a pivot of (1,0,0) and you then change the pivot to (-1,0,0), the result will be different. That's why you are getting this "jump". In your case, you might want to consider to use the rotateAxis()-method instead with the axis being the edge you want to rotate around. You have to calculate that edge based on your object's rotation matrix though.

How would I calculate the edge? I tried everything I could but I am unable to get that to do what I want. I was able to make it work on the desktop version by using this code:

private void rotateEdge(SimpleVector pivot, float angle, char axis) {
   cube.setRotationPivot(pivot);
   switch(axis) {                     

case 'x':
cube.rotateX(angle);
break;
case 'y':
cube.rotateY(angle);
break;
case 'z':
cube.rotateZ(angle);
break;
}
    cube.rotateMesh();
    cube.clearRotation();
}


In other words if I rotate the mesh and clear the rotation I could specify the pivot I wanted every time and it worked.

I would be ok with that solution (I don't need many rotations) but the thing is that it does not work on jpct-ae. On jpct-ae if I clear the rotation even after rotating the mesh nothing happens to the cube.

I am totally lost...

EgonOlsen

No, that doesn't work on AE by default, because AE uses compiled objects where the desktop version only does this if you advise it to. rotateMesh() changes the physical mesh of the object, it applies the rotation to the object in object space. It's not meant to be used for rotations at runtime (albeit for a simple cube, it might be ok).
If you want to use this, you have to make jPCT-AE know that the object's mesh might change at runtime. Add a call to cube.compile(true, true); before calling build() and a call to cube.touch() after the rotateMesh(). But even if this fixes the problem, it's actually the wrong solution IMHO. Anyway, if it works that way....

urubu

Adding those calls made it work!

I know it may not be the right solution but getting things working the way you expect (after weeks of trial and error)  is a very pleasant feeling and I can't resist the temptation of using even a  non recommended solution :-).

I think the problem that continues to be unsolved for me (without doing the above hack)  is how do I rotate an object (a cube in my case) using a pivot that is parallel to one of the axis once the object has been rotated.
Not sure if I am the only one interested in this but a good tutorial on rotateAxis() would be very helpful.

Thank you very much for your help.