如何在LibGDX中使用鼠标输入旋转对象?

时间:2018-12-14 23:47:07

标签: java 3d libgdx coordinate-transformation

我正在用LibGDX实现3D建模,我想用鼠标手动旋转对象,但是我找不到合适的教程和示例。

编辑:最初,我只问了有关旋转模型的问题,但我发现旋转照相机时也存在相同的问题。

Click here,以获取github.com中功能齐全的演示的源代码

以下是旋转视图和模型的快照:

snapshot

我希望对象沿鼠标拖动的方向旋转,无论当时碰巧是朝哪个方向旋转。现在,当我第一次向右拖动鼠标时,对象将按预期绕屏幕Y轴向右旋转;但是,当我向上拖动鼠标时,我希望对象绕屏幕X轴向上旋转,但它绕屏幕Z轴向左旋转。可以将它想像成一碗水中的浮球-无论您以哪种方式滑动它,它都会向该方向旋转。

在我看来,鼠标移动直接在其局部坐标系中转换了对象;但是相反,我认为它需要先将旋转轴本身从屏幕坐标系转换为对象坐标系,然后再将其应用于对象。我不知道,但这可能比这还要复杂。

我非常感谢您提供任何见解或帮助解决此问题;我的头发快要抽出来了...预先感谢。

2 个答案:

答案 0 :(得分:0)

LibGDX实现OpenGL。我们在OpenGL中使用的术语可以帮助我们了解LibGDX在后台如何工作。实现OpenGL的另一种技术是WebGL,WebGL使用JavaScript。 LibGDX使用Java。一旦我们知道OpenGL的工作原理,绘制对象和旋转对象就应该很有趣了。当然,这取决于我们要绘制的内容。 OpenGL很好 记录下来。 OpenGL本身总是一样的。第一个问题应该是我们要画什么?以及该项目的目标是什么。因此,您想绘制立方体并旋转它。凉。一旦我们可以绘制一个立方体并旋转它,就可以在场景中添加更多对象。凉。从策略上讲,您可以将项目分成多个部分。

  1. 绘制对象。
  2. 旋转它。
  3. 添加更多对象。
  4. 旋转它们。
  5. 我们完成了。

如果还想旋转视图,则可以使用与上面相同的过程进行一些修改。例如:

  • 绘制视图。
  • 在视图内部绘制对象。
  • 旋转对象。
  • 旋转视图。
  • 另一方面,您只需使用相机并在场景中移动它即可。
  • 完成。

更糟的是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...
}

//

旋转相机并旋转对象时,方向可能会改变,有时甚至会反转。在那个时间点,对象和相机所处的角度取决于。就像看着朝相反方向的检查镜一样。因此,用户在场景中的位置和方向也很重要。

//

space model

//

当您查看围绕圆旋转“ <---”的对象时,逻辑上它将改变方向“ --->”。当到达远端“ <--->”时。即从右到左和从左到右。当然,用户仍将使用相同的按钮。当您按下其他按钮时,遵循相同的逻辑。不同的旋转顺序也可以产生不同的图像。另一个耗时的选择是:translate(rotate(scale(scale(geometry))))。最终,整个图像将是一个由其各个部分组成的整体。此过程可以帮助您调试代码,并找出导致错误的原因。有了一点数学,事情就不会恶化。旋转的物体背后的科学也很重要。当您看着旋转的物体时,美丽就在观察者的眼睛上。例如您在看正面还是背面?最后,您必须使用数学来使模型表现出想要的行为。

享受。


答案 1 :(得分:0)

我在其他姐妹论坛之一上问了同样的问题,并得到了我能够实现的答案。

See the discussion here.

这是对代码的更改,使整个事情正常运行:

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 ];
            }
        }
    }
}