我正在用LibGDX实现3D建模,我想用鼠标手动旋转对象,但是我找不到合适的教程和示例。
编辑:最初,我只问了有关旋转模型的问题,但我发现旋转照相机时也存在相同的问题。
Click here,以获取github.com中功能齐全的演示的源代码
以下是旋转视图和模型的快照:
我希望对象沿鼠标拖动的方向旋转,无论当时碰巧是朝哪个方向旋转。现在,当我第一次向右拖动鼠标时,对象将按预期绕屏幕Y轴向右旋转;但是,当我向上拖动鼠标时,我希望对象绕屏幕X轴向上旋转,但它绕屏幕Z轴向左旋转。可以将它想像成一碗水中的浮球-无论您以哪种方式滑动它,它都会向该方向旋转。
在我看来,鼠标移动直接在其局部坐标系中转换了对象;但是相反,我认为它需要先将旋转轴本身从屏幕坐标系转换为对象坐标系,然后再将其应用于对象。我不知道,但这可能比这还要复杂。
我非常感谢您提供任何见解或帮助解决此问题;我的头发快要抽出来了...预先感谢。
答案 0 :(得分:0)
LibGDX实现OpenGL。我们在OpenGL中使用的术语可以帮助我们了解LibGDX在后台如何工作。实现OpenGL的另一种技术是WebGL,WebGL使用JavaScript。 LibGDX使用Java。一旦我们知道OpenGL的工作原理,绘制对象和旋转对象就应该很有趣了。当然,这取决于我们要绘制的内容。 OpenGL很好 记录下来。 OpenGL本身总是一样的。第一个问题应该是我们要画什么?以及该项目的目标是什么。因此,您想绘制立方体并旋转它。凉。一旦我们可以绘制一个立方体并旋转它,就可以在场景中添加更多对象。凉。从策略上讲,您可以将项目分成多个部分。
如果还想旋转视图,则可以使用与上面相同的过程进行一些修改。例如:
更糟的是LibGDX可以扩展许多不同的类,程序员必须实现所有抽象方法。根据您在项目中扩展或实现的类,您的代码可能看起来不同或某些函数的行为不同。有关这些类的文档是随机的。每个抽象类都有其抽象方法。程序员应使用dispose()方法释放LibGDX分配的任何其他资源。只需进行少量更改,您的代码即可按预期工作。
例如:
//
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputProcessor;
//import etc...
public class Space extends ApplicationAdapter implements ApplicationListener, InputProcessor {
SpriteBatch batch;
BitmapFont font;
float backgroundColor[];
Cube cubes[];
ModelBatch modelBatch;
int selectedCube;
PerspectiveCamera camera;
Environment environment;
int xCubes = 27;
int touchedButton;
int lastX, lastY;
float Theta, Phi, dX, dY;
Vector3 screenAOR;
float screenAng;
float point[];
int side[];
int front[];
float w;
float h;
Model viewM;
ModelInstance viewMin;
Vector3 position;
ColorAttribute attrib;
Vector3 cubePositions[];
boolean drag;
@Override
public void create () {
drag = false;
Theta = 0;
Phi = 0;
batch = new SpriteBatch();
font = new BitmapFont();
font.setColor(Color.FOREST);
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
modelBatch = new ModelBatch();
screenAOR = new Vector3();
camera = new PerspectiveCamera(67f, 3f, 2f);
camera.position.set(10f, -10f, 70f);
camera.lookAt(Vector3.Zero);
camera.up.set(Vector3.Y);
camera.near = 1f;
camera.far = 500f;
camera.update();
backgroundColor = new float[]{.9f, .9f, .7f};
environment = new Environment();
environment.set(new ColorAttribute( ColorAttribute.AmbientLight, .6f, .6f, .6f, 1f));
environment.add(new DirectionalLight().set(.8f, .8f, .8f, 50f, 50f, 50f));
environment.add(new DirectionalLight().set(.5f, .5f, .5f, -50f, -50f, 50f));
spaceModel();
Gdx.input.setInputProcessor(this);
//Gdx.graphics.requestRendering();
}
@Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
//Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//Gdx.gl.glClearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
Gdx.gl.glEnable(GL20.GL_CULL_FACE);
batch.begin();
modelBatch.begin(camera);
modelBatch.render(viewMin, environment);
for(int i = 0; i < cubes.length; i++){
modelBatch.render(cubes[i].modelInstance, environment);
}
font.draw(batch, "Space pro...", 10, 100);
batch.end();
modelBatch.end();
}
@Override
public void dispose () {
batch.dispose();
modelBatch.dispose();
font.dispose();
viewM.dispose();
}
/*///////////////////////////////////
//Implements all abstract methods.
//
*/////////////////////////////////
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
//lastX -= screenX;
//lastY -= screenY;
//float aspRatio = w/h;
//float angle = 40.0f;
moveModel(screenX, screenY);
// distance of mouse movement
//screenAng = (float) Math.sqrt( ((lastX * lastX) + (lastY * lastY)) );
//screenAng = (float) Math.tan((angle * 0.5)* (Math.PI/180));
// direction vector of the AOR
//screenAOR.set((lastY/screenAng), (lastX/screenAng), 0f );
//screenAOR.set(projection(angle,aspRatio,h,w));
//public Vector3 set(float x, float y, float z)
screenAOR.set(dX, dY, 0f);
if ( touchedButton == 0 ){
//public Matrix4 rotate(Vector3 axis, float degrees)
//cubes[ selectedCube ].modelInstance.transform.rotate( screenAOR, screenAng );
//public Matrix4 rotate(float axisX, float axisY, float axisZ, float degrees)
cubes[ selectedCube ].modelInstance.transform.rotate(dX, dY, 0f, Theta);
cubes[ selectedCube ].modelInstance.transform.rotate(dX, dY, 0f, Phi);
}
else{
//public void rotateAround(Vector3 point, Vector3 axis, float angle)
//camera.rotateAround( Vector3.Zero, screenAOR, (-screenAng/5.5f) );
//public void rotate(float angle, float axisX, float axisY, float axisZ)
//camera.rotate(Theta, dX, dY, 0f);
//camera.rotate(Phi, dX, dY, 0f);
//camera.rotateAround(position, screenAOR, Theta);
camera.rotateAround(Vector3.Zero, screenAOR, Theta);
camera.update();
//camera.rotateAround(position, screenAOR, Phi);
camera.rotateAround(Vector3.Zero, screenAOR, Phi);
camera.update();
viewMin.transform.rotate(dX, dY, 0f, Theta);
viewMin.transform.rotate(dX, dY, 0f, Phi);
}
//Gdx.graphics.requestRendering();
//Gdx.app.log("touchDragged:", screenAng+" : "+screenAOR+" : "+touchedButton);
return true;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
drag = true;
if(button == Buttons.LEFT){
touchedButton = 0;
}
else{
touchedButton = button;
}
Gdx.app.log("touchDown:", button+" : "+screenX+" : "+screenY+" : "+pointer);
return true;
}
@Override
public boolean keyDown(int i) {
float move = 1.0f;
float pX = w/10;
float pY = h/10;
if(i == Keys.LEFT){
pX -= move;
//public void rotate(float angle, float axisX, float axisY, float axisZ)
//camera.rotate(-45, pX, 0f, 0f);
camera.rotate(-45, 0f, pY, 0f);
//camera.update();
//public void translate(float x, float y, float z)
//camera.translate(move, 0f, 0f);
}
if(i == Keys.RIGHT){
pX += move;
//camera.rotate(45, pX, 0f, 0f);
camera.rotate(45, 0f, pY, 0f);
//camera.update();
//camera.translate(-move, 0f, 0f);
}
if(i == Keys.DOWN){
pY -= move;
//camera.rotate(-45, 0f, pY, 0f);
//camera.rotate(-45, pX, 0f, 0f);
camera.rotate(45, pX, 0f, 0f);
//camera.update();
//camera.translate(0f, 0f, move);
//camera.update();
//camera.translate(0f, move, 0f);
}
if(i == Keys.UP){
pY += move;
//camera.rotate(45, 0f, pY, 0f);
//camera.rotate(45, pX, 0f, 0f);
camera.rotate(-45, pX, 0f, 0f);
//camera.update();
//camera.translate(0f, 0f, -move);
//camera.update();
//camera.translate(0f, -move, 0f);
}
camera.update();
Gdx.app.log("KeyDown: ", pX+" : "+pY+" : "+i);
return true;
}
@Override
public boolean keyUp(int i) {
Gdx.app.log("KeyUp: ",i+" : ");
return false;
}
@Override
public boolean keyTyped(char c) {
//Gdx.app.log("KeyTyped: ",c+" : ");
return false;
}
@Override
public boolean touchUp(int i, int i1, int i2, int i3) {
drag = false;
Gdx.app.log("touchUp: ",i+" : "+i1+" : "+i2+" : "+i3);
return true;
}
@Override
public boolean mouseMoved(int i, int i1) {
if(!drag)
{
dX *= 0.96;
dY *= 0.96;
Theta += dX;
Phi += dY;
return false;
}
Gdx.app.log("mouseMoved: ", i+" : "+i1);
return false;
}
@Override
public boolean scrolled(int i) {
return false;
}
public void moveModel(int x2, int y2){
dX = (float) ((x2 - lastX)*2*(Math.PI/w));
dY = (float) ((y2 - lastY)*2*(Math.PI/h));
Theta += dX;
Phi += dY;
lastX = x2;
lastY = y2;
}
public void spaceModel(){
xCubes = 27;
selectedCube = 14;
ModelBuilder modelB = new ModelBuilder();
attrib = new ColorAttribute(1,Color.VIOLET);
Material m = new Material();
m.set(attrib);
//public Model createXYZCoordinates(float axisLength, Material material, long attributes)
viewM = modelB.createXYZCoordinates(w, m , 1);
cubePositions = new Vector3[xCubes];
for(int i = 0; i < xCubes; i++){
cubePositions[i] = new Vector3((i/9), ((i%9)/3), (i%3)).scl(20f).add(-20f, -20f, -20f);
}
cubes = new Cube[xCubes];
for(int i = 0; i < xCubes; i++){
cubes[i] = new Cube(cubePositions[i], (i == selectedCube));
}
viewMin = new ModelInstance(viewM);
position = cubePositions[0];
viewMin.transform.setTranslation(position);
Gdx.app.log("viewModel: ", w+" : "+h+" : "+w/h);
}
float[] projection(float angle, float a, float z1, float z2){
float ang = (float) Math.tan((angle * 0.5)* (Math.PI/180));
float[] proj = {
(float)0.5/ang, 0, 0, 0,
0, (float)0.5*a/ang, 0, 0,
0, 0, -(z2+z1)/(z2-z1), -1,
0, 0, (-2*z2*z1)/(z2-z1), 0
};
return proj;
}
}
/*////////////////////
//Draw cubes.
//
*/////////////////
class Cube{
Vector3 position;
Model model;
ModelInstance modelInstance;
Cube(Vector3 cubePosition, boolean selected) {
position = cubePosition;
compose(selected);
}
//etc...
}
//
旋转相机并旋转对象时,方向可能会改变,有时甚至会反转。在那个时间点,对象和相机所处的角度取决于。就像看着朝相反方向的检查镜一样。因此,用户在场景中的位置和方向也很重要。
//
//
当您查看围绕圆旋转“ <---”的对象时,逻辑上它将改变方向“ --->”。当到达远端“ <--->”时。即从右到左和从左到右。当然,用户仍将使用相同的按钮。当您按下其他按钮时,遵循相同的逻辑。不同的旋转顺序也可以产生不同的图像。另一个耗时的选择是:translate(rotate(scale(scale(geometry))))。最终,整个图像将是一个由其各个部分组成的整体。此过程可以帮助您调试代码,并找出导致错误的原因。有了一点数学,事情就不会恶化。旋转的物体背后的科学也很重要。当您看着旋转的物体时,美丽就在观察者的眼睛上。例如您在看正面还是背面?最后,您必须使用数学来使模型表现出想要的行为。
享受。
答案 1 :(得分:0)
我在其他姐妹论坛之一上问了同样的问题,并得到了我能够实现的答案。
这是对代码的更改,使整个事情正常运行:
Here is the change to the code that made the whole thing work correctly:
@Override public boolean touchDragged( int screenX, int screenY, int pointer )
{
lastX -= screenX;
lastY -= screenY;
// distance of mouse movement
screenAng = Vector3.len( lastX, lastY, 0f );
// direction vector of the AOR
screenAOR.set( lastY/screenAng, lastX/screenAng, 0f );
if ( touchedButton == 0 )
{ // rotate the part
// transform the screen AOR to a model rotation
Matrix4 camT, camR, camRi, modT, modR;
camT = new Matrix4();
camR = new Matrix4();
modT = new Matrix4();
modR = new Matrix4();
decompose( camera.view, camT, camR );
camRi = camR.cpy().inv();
decompose( cubes[ selectedCube ].modelInstance.transform, modT, modR );
tempMat.idt()
.mul( modT )
.mul( camRi )
.rotate( screenAOR, -screenAng )
.mul( camR )
.mul( modR );
cubes[ selectedCube ].modelInstance.transform.set( tempMat );
}
else if ( touchedButton == 1 )
{ // rotate the camera
// transform the AOR from screen CS to camera CS
// get the camera transformation matrix
tempMat.set( camera.view );
tempMat.translate( camera.position );
tempMat.inv();
// transform the screen AOR to a world AOR
worldAOR = transform( tempMat, screenAOR, worldAOR ).nor();
// apply the rotation of the angle about the world AOR to the camera
camera.rotateAround( Vector3.Zero, worldAOR, screenAng/5.5f );
camera.update();
}
lastX = screenX;
lastY = screenY;
Gdx.graphics.requestRendering();
return true;
}
Vector3 transform( Matrix4 mat, Vector3 from, Vector3 to )
{
// transform a vector according to a transformation matrix
to.x = from.dot( mat.val[ Matrix4.M00 ], mat.val[ Matrix4.M01 ],
mat.val[ Matrix4.M02 ] )+mat.val[ Matrix4.M03 ];
to.y = from.dot( mat.val[ Matrix4.M10 ], mat.val[ Matrix4.M11 ],
mat.val[ Matrix4.M12 ] )+mat.val[ Matrix4.M13 ];
to.z = from.dot( mat.val[ Matrix4.M20 ], mat.val[ Matrix4.M21 ],
mat.val[ Matrix4.M22 ] )+mat.val[ Matrix4.M23 ];
return to;
}
void decompose( Matrix4 m, Matrix4 t, Matrix4 r )
{
Matrix4 I4 = new Matrix4(); // Identity
for ( int i = 0; i < 4; i++ )
{
for ( int j = 0; j < 4; j++ )
{
if (i == 3 || j == 3)
{
r.val[ i*4+j ] = I4.val[ i*4+j ];
t.val[ i*4+j ] = m.val[ i*4+j ];
}
else
{
r.val[ i*4+j ] = m.val[ i*4+j ];
t.val[ i*4+j ] = I4.val[ i*4+j ];
}
}
}
}