java - libgdx 如何在触摸时缩放和旋转 3d 模型

标签 java libgdx

我正在尝试创建一个简单的应用程序,它允许您从 .obj 加载 3d 模型并通过触摸屏幕缩放/旋转它。

我设法编写了从文件加载 3d 模型并检测手势的代码,但现在我不确定如何通过触摸屏幕启用缩放/旋转功能。

这是我现在的代码:

public class RenderObjApp implements ApplicationListener, GestureDetector.GestureListener {
    public static int SCREEN_WIDTH = 800;
    public static int SCREEN_HEIGHT = 600;

    private static final String TAG = RenderObjApp.class.getSimpleName();

    private Mesh model;
    private PerspectiveCamera camera;

    private float scale = 1f;

    @Override
    public void create() {
        model = ObjLoader.loadObj(Gdx.files.internal("data/cessna.obj").read(), true);
        Gdx.gl.glEnable(GL10.GL_DEPTH_TEST);
        Gdx.input.setInputProcessor(new GestureDetector(this));
    }

    @Override
    public void dispose() {
    }

    @Override
    public void pause() {
    }


    @Override
    public void render() {
        Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        camera.update();
        camera.apply(Gdx.gl10);
        model.render(GL10.GL_TRIANGLES);
    }

    @Override
    public void resize(int arg0, int arg1) {
        float aspectRatio = (float) arg0 / (float) arg1;
        camera = new PerspectiveCamera(75, 2f * aspectRatio, 2f);
        camera.near = 0.1f;
        camera.translate(0, 0, 0);
    }

    @Override
    public void resume() {
    }

    @Override
    public boolean touchDown(float x, float y, int pointer) {
        Gdx.app.log(TAG, "touchDown: ");
        return false;
    }

    @Override
    public boolean tap(float x, float y, int count, int pointer, int button) {
        Gdx.app.log(TAG, "tap: ");   
        return false;
    }

    @Override
    public boolean longPress(float x, float y) {
        Gdx.app.log(TAG, "zoom: ");
        return false;
    }

    @Override
    public boolean fling(float velocityX, float velocityY, int pointer, int button) {
        Gdx.app.log(TAG, "fling: ");
        return false;
    }

    @Override
    public boolean pan(float x, float y, float deltaX, float deltaY) {
        Gdx.app.log(TAG, "pan: ");
        return false;
    }

    @Override
    public boolean zoom(float initialDistance, float distance) {
        Gdx.app.log(TAG, "zoom: initialDistance=" + initialDistance + ", distance=" + distance);
        return false;
    }

    @Override
    public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
        Gdx.app.log(TAG, "pinch: ");
        return false;
    }
}

所以我正在研究如何旋转 PerspectiveCamera 和 Mesh 本身。

最佳答案

我一直在研究一种“Blender 风格”的相机,它具有双指缩放功能以及(在桌面上)Blender 相机的大部分功能。这是一项正在进行的工作——它还没有完全模仿 Blender 相机的行为(目前)。我认为这会让你指向正确的方向。你应该知道的一些事情:

  1. 您可能需要平移模型,使其位于原点。除非您平移相机,否则它会一直指向原点。 (您只能在桌面上翻译,目前还不能在安卓上翻译);

  2. 我在此处获得了大部分双指缩放处理代码:https://code.google.com/p/libgdx-users/wiki/PinchToZoom .

  3. 抱歉出现了魔数(Magic Number)。我将在未来使这些常量成为常量。

  4. 如果您或其他任何人改进了此代码,请与我分享一份副本。

抽象类:

/* Author: Christopher Grabowski, yourchristopher6334 gmail.com */

package ...;

import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Vector2;

abstract public class ControllableCamera extends PerspectiveCamera implements InputProcessor{

    abstract public void resize(int width, int height);
    abstract public void render();

    public ControllableCamera(int fieldOfView, int width, int height) {
        super(fieldOfView, width, height);
    }

    @Override
    public boolean keyDown(int keyCode) {
        return false;
    }

    @Override
    public boolean keyTyped(char arg0) {
        return false;
    }

    @Override
    public boolean keyUp(int arg0) {
        return false;
    }

    @Override
    public boolean touchDown(int x, int y, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override
    public boolean touchUp(int x, int y, int pointer, int button) {
        return false;
    }

    @Override
    public boolean mouseMoved(int arg0, int arg1) {
        return false;
    }

    @Override
    public boolean scrolled(int direction) {
        return false;
    }
}

具体类:

    /* Author: Christopher Grabowski, yourchristopher6334 gmail.com */

package ...;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.math.Vector3;

/*
 * the pause, resize, and render methods must be called within their corresponding
 * methods in the ApplicationListener
 */

public class BlenderStyleCamera extends ControllableCamera {
    public static final Vector3 ORIGIN = new Vector3(0, 0, 0);

    private static boolean shiftIsPressed = false, controlIsPressed = false,
            isScrollingUp = false, isScrollingDown = false,
            isSingleTouched = false, justSingleTouched = false;

    private float aspectRatio;
    private int x = -1, y = -1;
    private float dx = 0.0f, dy = 0.0f;
    private final Vector3 tmp = new Vector3();

    // fields related to pinch-to-zoom
    private int numberOfFingers = 0;
    private int fingerOnePointer;
    private int fingerTwoPointer;
    private float lastDistance = 0;
    private final Vector3 fingerOne = new Vector3();
    private final Vector3 fingerTwo = new Vector3();

    public BlenderStyleCamera(int fieldOfView, int width, int height) {
        super(fieldOfView, width, height);
        aspectRatio = viewportHeight / viewportWidth;
        Gdx.input.setInputProcessor(this);
        up.set(0.0f, 1.0f, 0.0f);
        position.set(0.0f, 0.0f, 30.0f);
        far = 300.0f;
        lookAt(0, 0, 0);
        translate(0.0f, 0.0f, 2.1f);
        lookAt(0, 0, 0);
        update();
    }

    public void pause() {
        numberOfFingers = 0;
    }

    @Override
    public void resize(int width, int height) {
        viewportWidth = width;
        viewportHeight = height;
        aspectRatio = viewportHeight / viewportWidth;
        update();
    }

    @Override
    public void render() {
        if (isSingleTouched) {

            // This gets the change in touch position and
            // compensates for the aspect ratio.
            if (x == -1 || y == -1 || justSingleTouched) {
                x = Gdx.input.getX();
                y = Gdx.input.getY();
            } else {
                dx = (x - Gdx.input.getX());
                dy = (y - Gdx.input.getY()) / aspectRatio;
            }

            // This zooms when control is pressed.
            if (controlIsPressed && dy > 0) {
                scrollIn();
            } else if (controlIsPressed && dy < 0) {
                scrollOut();
            }

            // This translates the camera blender-style
            // if shift is pressed.
            // Note that this will look weird with a
            // perspective camera.
            else if (shiftIsPressed) {
                translateTangentially();
            }

            // Default is to rotate the object
            // (actually rotate the camera about a sphere
            // that surrounds the object).
            else {
                travelAround();
            }

            x = Gdx.input.getX();
            y = Gdx.input.getY();

            justSingleTouched = false;
        }

        // this zooms when the mouse wheel is rotated
        if (isScrollingUp) {
            scrollIn();
            isScrollingUp = false;
        } else if (isScrollingDown) {
            scrollOut();
            isScrollingDown = false;
        }

        // Some key controls
        if (Gdx.input.isKeyPressed(Keys.LEFT) || Gdx.input.isKeyPressed(Keys.A)) {
            translateTangentially(1, 0);
        } else if (Gdx.input.isKeyPressed(Keys.RIGHT)
                || Gdx.input.isKeyPressed(Keys.D)) {
            translateTangentially(-1, 0);
        }

        if (Gdx.input.isKeyPressed(Keys.UP) || Gdx.input.isKeyPressed(Keys.W)) {
            translateTangentially(0, 1);
        } else if (Gdx.input.isKeyPressed(Keys.DOWN)
                || Gdx.input.isKeyPressed(Keys.S)) {
            translateTangentially(0, -1);
        }

        update();
    }

    // These methods create the pinch zoom
    // and set some flags for logic in render method.
    @Override
    public boolean touchDown(int x, int y, int pointer, int button) {
        // for pinch-to-zoom
        numberOfFingers++;
        if (numberOfFingers == 1) {
            isSingleTouched = true;
            justSingleTouched = true;
            fingerOnePointer = pointer;
            fingerOne.set(x, y, 0);
        } else if (numberOfFingers == 2) {
            isSingleTouched = false;
            fingerTwoPointer = pointer;
            fingerTwo.set(x, y, 0);

            float distance = fingerOne.dst(fingerTwo);
            lastDistance = distance;
        }
        return true;
    }

    @Override
    public boolean touchDragged(int x, int y, int pointer) {
        if (numberOfFingers > 1) {
            if (pointer == fingerOnePointer) {
                fingerOne.set(x, y, 0);
            }
            if (pointer == fingerTwoPointer) {
                fingerTwo.set(x, y, 0);
            }

            float distance = fingerOne.dst(fingerTwo);

            if (lastDistance > distance) {
                scrollOut();
            } else if (lastDistance < distance) {
                scrollIn();
            }

            lastDistance = distance;
            update();
        }
        return true;
    }

    @Override
    public boolean touchUp(int x, int y, int pointer, int button) {
        isSingleTouched = false;
        if (numberOfFingers == 1) {
            Vector3 touchPoint = new Vector3(x, y, 0);
            unproject(touchPoint);
        }
        numberOfFingers--;

        // just some error prevention... clamping number of fingers (ouch! :-)
        if (numberOfFingers < 0) {
            numberOfFingers = 0;
        }

        lastDistance = 0;
        return false;
    }   

    // These methods set flags for logic in render method.
    @Override
    public boolean keyDown(int keycode) {

        switch (keycode) {
        case (Keys.SHIFT_LEFT):
        case (Keys.SHIFT_RIGHT):
            shiftIsPressed = true;
            break;
        case (Keys.CONTROL_LEFT):
        case (Keys.CONTROL_RIGHT):
            controlIsPressed = true;
            break;
        case (Keys.O):
            this.up.set(0.0f, 1.0f, 0.0f);
            this.position.set(0.0f, 0.0f, 30.0f);
            this.lookAt(0, 0, 0);
            this.update();
        }
        return true;
    }

    @Override
    public boolean keyUp(int arg0) {
        shiftIsPressed = controlIsPressed = false;
        return true;
    }

    @Override
    public boolean scrolled(int direction) {
        if (direction == -1) {
            isScrollingUp = true;
        } else if (direction == 1) {
            isScrollingDown = true;
        }
        return true;
    }

    // The rest of the methods translate the camera.
    public void scrollIn() {
        float magnitude = 1.0f;
        scrollIn(magnitude);
    }

    public void scrollIn(float magnitude) {
        if (position.dst2(ORIGIN) > 2.0f) {
            tmp.set(position);
            tmp.nor();
            this.translate(-tmp.x * magnitude, -tmp.y * magnitude, -tmp.z
                    * magnitude);
            update();
        }
    }

    public void scrollOut() {
        float magnitude = 1.0f;
        scrollOut(magnitude);
    }

    public void scrollOut(float magnitude) {
        tmp.set(position);
        tmp.nor();
        this.translate(tmp.x * magnitude, tmp.y * magnitude, tmp.z * magnitude);
        update();
    }

    private void travelAround() {
        tmp.set(up);
        rotateAround(ORIGIN, tmp, dx);
        tmp.crs(position).nor();
        rotateAround(ORIGIN, tmp, dy);
    }

    private void translateTangentially() {
        translateTangentially(dx, dy);
    }

    private void translateTangentially(float dx, float dy) {
        tmp.set(up);
        tmp.crs(position);
        if (dx > 0) {
            translate(tmp.x / 15.0f, tmp.y / 15.0f, tmp.z / 15.0f);
        } else if (dx < 0) {
            translate(-tmp.x / 15.0f, -tmp.y / 15.0f, -tmp.z / 15.0f);
        }

        if (dy > 0) {
            translate(-up.x, -up.y, -up.z);
        } else if (dy < 0) {
            translate(up);
        }
    }

}

关于java - libgdx 如何在触摸时缩放和旋转 3d 模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13420787/

相关文章:

java - LibGDX 和 RoboVM Maven 插件的问题

java - Libgdx,如何从坐标创建矩形?

android - libGDX - 从图像按钮中删除颜色

crash - 不幸的是,应用程序已停止工作 libGDX 应用程序

java - Tomcat并发

java - 如何使用 Mockito 和 catch-Exception 断言 void 方法抛出异常?

java - Oracle SQL 中如何处理单引号?

java - Hadoop YARN 上的 JMH 基准测试

java - 检查Firebase数据库中是否存在值

java - 无法切换屏幕 [Libgdx]