Как вращать объекты с помощью мыши в LibGDX? - PullRequest
0 голосов
/ 15 декабря 2018

Я реализую трехмерное моделирование с помощью LibGDX, и я хочу вручную вращать объекты с помощью мыши, но я просто не могу найти какие-либо учебные пособия и примеры, которые являются правильными.

РЕДАКТИРОВАТЬ: Первоначально я только спросилэтот вопрос касается вращающихся моделей, но я обнаружил, что та же проблема существует при вращении камеры.

Нажмите здесь , чтобы получить исходный код полнофункциональной демонстрационной версии на github.com

Вот снимок поворота вида и модели:

snapshot

Я хочу, чтобы объект вращался в направлении, в котором он перемещается мышьюНезависимо от того, в каком направлении он ориентирован в данный момент.Как и сейчас, когда я впервые перетаскиваю мышь вправо, объект поворачивается вправо вокруг оси Y экрана, как и ожидалось;но затем, когда я перетаскиваю мышь вверх, я хочу, чтобы объект вращался вверх вокруг оси X экрана, но вместо этого он вращается влево вокруг оси Z экрана.Думайте об этом, как о плавающем шаре в миске с водой - каким бы способом вы не ударили по нему, он вращается в этом направлении.

Мне кажется, что движение мыши преобразует объекты непосредственно в их локальной системе координат;но вместо этого я думаю, что нужно преобразовать саму ось вращения из системы координат экрана в систему координат объекта, прежде чем применять ее к объекту.Я не знаю, но это может быть даже сложнее, чем это.

Я был бы очень признателен за любое понимание или помощь, чтобы решить эту проблему;У меня кончились волосы, чтобы вытащить ... Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 30 декабря 2018

Я задал тот же вопрос на одном из других родственных форумов и получил ответ, который смог реализовать.

См. Обсуждение здесь.

Вот изменение в коде, которое заставило все это работать правильно:

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 ];
            }
        }
    }
}
0 голосов
/ 27 декабря 2018

LibGDX реализует OpenGL.Терминология, которую мы используем в OpenGL, может помочь нам узнать, как LibGDX работает за кулисами.Другая технология, которая реализует OpenGL - это WebGL, WebGL использует JavaScript.LibGDX использует Java.Как только мы узнаем, как работает OpenGL, рисовать объекты и вращающиеся объекты должно быть весело.Конечно, в зависимости от того, что мы рисуем.OpenGL хорошо документирован.Сам OpenGL всегда работает одинаково.Первый вопрос, должно быть, что мы рисуем?И каковы цели проекта.Итак, вы хотите нарисовать куб и вращать его.Здорово.Как только мы можем нарисовать один куб и повернуть его, мы можем добавить больше объектов в сцену.Здорово.Стратегически вы можете разделить ваш проект на части.

  1. Нарисуйте свой объект.
  2. Поверните его.
  3. Добавьте больше объектов.
  4. Поверните их.
  5. Мы закончили.

Если вы также хотите повернуть вид, вы можете использовать тот же процесс, что и выше, с некоторыми изменениями.Например:

  • Нарисуйте вид.
  • Нарисуйте объекты внутри вида.
  • Поверните объекты.
  • Поверните вид.
  • С другой стороны, вы можете просто использовать камеру и перемещать ее по сцене.
  • Готово.

Что еще хуже, LibGDX может расширять множество различных классов,Программист должен реализовать все абстрактные методы.Ваш код может выглядеть по-разному, или некоторые функции ведут себя по-разному, в зависимости от того, какой класс вы расширяете или реализуете в своем проекте.Документация об этих классах является стохастической.Каждый абстрактный класс поставляется со своими абстрактными методами.Программист должен освободить любые другие ресурсы, выделенные LibGDX, используя метод dispose ().С помощью всего лишь нескольких изменений ваш код должен работать должным образом.

Например:

//
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

//

Когдавы смотрите на объект, вращающийся «<---» по кругу, логически он будет менять направление «--->».Когда он достигает дальнего конца «<--->».т.е. справа налево и слева направо.Конечно, пользователь все еще будет использовать ту же кнопку.При нажатии других кнопок следует та же логика.Различные последовательности поворотов также могут приводить к разным изображениям.Другой вариант, который требует много времени: перевести (повернуть (масштаб (геометрия))).В конце концов, все изображение будет единым целым, состоящим из разных частей.Этот процесс мог бы помочь вам отладить ваш код и выяснить, что вызвало ошибки.С небольшим количеством математики хуже не может быть.Наука, стоящая за объектом, который вы вращаете, также важна.Когда вы смотрите на вращающийся объект, красота находится на глазах у наблюдателей.Например, вы смотрите на лицевую или оборотную сторону?Наконец, вы должны использовать математику, чтобы заставить вашу модель вести себя так, как вы хотите.

Наслаждайтесь.


...