MultiTouch camera controls source code
http://www.jpct.net/wiki/index.php/MultiTouch_camera_controls
MultiTouch camera controls
This code will handle control from 1 or 2 fingers, it was made for a tablet showing 3D objects, so it's made for navigating not for FPS gaming.
Usage Call MovementHandler.handleMotionEvent for every motionEvent you receive. CAll MovementHandler.getMovement for every tick or whenever you can to update the movement.
import android.util.Log;
import android.view.MotionEvent; /**
*
* @author Ruben.Hesselbaek@beumergroup.com
*
*/
public class MovementHandler { /**
* Logs a lot of stuff
*/
public static final boolean DEBUG_MOVEMENT = false; /**
* Min 0.25 degrees
*/
public static final float MIN_ANGLE_XMOVEMENT = (float) (Math.PI / 720); /**
* Max 15 degrees
*/
public static final float MAX_ANGLE_XMOVEMENT = (float) MIN_ANGLE_XMOVEMENT * 60; /**
* Pixels->Angle ratio on the X-axis
*/
public static final float XANGLE_FACTOR = 360; /**
* Min 0.25 degrees
*/
public static final float MIN_ANGLE_YMOVEMENT = (float) (Math.PI / 720); /**
* Max 15 degrees
*/
public static final float MAX_ANGLE_YMOVEMENT = (float) MIN_ANGLE_YMOVEMENT * 60; /**
* Pixels->Angle ratio on the Y-axis
*/
public static final float YANGLE_FACTOR = 360; /**
* Pixels->Distance ratio
*/
public static final float DISTANCE_FACTOR = 10; /**
* Pixels->Movement ratio
*/
public static final float MOVEMENT_X_FACTOR = 10; /**
* Pixels->Movement ratio
*/
public static final float MOVEMENT_Y_FACTOR = 10; public static final float MIN_MOVEMENT_DELTA = 0.1f;
public static final float MAX_MOVEMENT_DELTA = 5; private int _firstPointerId = MotionEvent.INVALID_POINTER_ID;
private float startMovementx1;
private float startMovementy1;
private float lastMovementx1;
private float lastMovementy1; private int _secondPointerId = MotionEvent.INVALID_POINTER_ID;
private float startMovementx2;
private float startMovementy2;
private float lastMovementy2;
private float lastMovementx2; private float startDistance;
private float lastDistance; private float startAngle;
private float lastAngle; public synchronized Movement getMovement() {
Movement movement = new Movement(); if (_secondPointerId != MotionEvent.INVALID_POINTER_ID) {
float movementX1 = lastMovementx1 - startMovementx1;
float movementY1 = lastMovementy1 - startMovementy1;
float movementX2 = lastMovementx2 - startMovementx2;
float movementY2 = lastMovementy2 - startMovementy2; if ((movementX1 > 0 && movementX2 > 0)
|| (movementX1 < 0 && movementX2 < 0)) {
float movementX = Math.min(Math.abs(movementX1),
Math.abs(movementX2))
/ MOVEMENT_X_FACTOR;
if (movementX > MIN_MOVEMENT_DELTA
&& movementX < MAX_MOVEMENT_DELTA) {
if (movementX1 > 0) {
movement.cameraMovementX = movementX;
} else {
movement.cameraMovementX = -movementX;
}
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementX=<"
+ movementX + ">, movementX1=<"
+ movementX1 + ">, movementX2=<"
+ movementX2 + ">");
}
}
} if ((movementY1 > 0 && movementY2 > 0)
|| (movementY1 < 0 && movementY2 < 0)) {
float movementY = Math.min(Math.abs(movementY1),
Math.abs(movementY2))
/ MOVEMENT_Y_FACTOR;
if (movementY > MIN_MOVEMENT_DELTA
&& movementY < MAX_MOVEMENT_DELTA) {
if (movementY1 > 0) {
movement.cameraMovementY = movementY;
} else {
movement.cameraMovementY = -movementY;
}
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementY<"
+ movementY + ">, movementY1=<"
+ movementY1 + ">, movementY2=<"
+ movementY2 + ">");
}
}
}
float distanceDelta = lastDistance - startDistance;
float distance = distanceDelta / DISTANCE_FACTOR;
float absoluteDistance = Math.abs(distance);
if (absoluteDistance > MIN_MOVEMENT_DELTA
&& absoluteDistance < MAX_MOVEMENT_DELTA) {
movement.cameraMovementZ = distance;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: distanceDelta=<"
+ distanceDelta + ">, distance=<"
+ distance + ">");
}
} float angleDelta = lastAngle - startAngle;
float absoluteAngle = Math.abs(angleDelta);
if (absoluteAngle > MIN_ANGLE_XMOVEMENT
&& absoluteAngle < MAX_ANGLE_XMOVEMENT) {
movement.cameraRotationY = angleDelta;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: absoluteAngle=<"
+ absoluteAngle + ">, startAngle=<"
+ Math.toDegrees(startAngle)
+ ">, lastAngle=<"
+ Math.toDegrees(lastAngle) + ">");
}
}
} else if (_firstPointerId != MotionEvent.INVALID_POINTER_ID) {
float movementX1 = lastMovementx1 - startMovementx1;
float angle = (float) ((movementX1) / XANGLE_FACTOR);
float absoluteAngle = Math.abs(angle);
if (absoluteAngle > MIN_ANGLE_XMOVEMENT
&& absoluteAngle < MAX_ANGLE_XMOVEMENT) {
movement.cameraRotationY = angle;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementX=<"
+ movementX1 + ">, angle=<" + absoluteAngle
+ ">");
}
} float movementY1 = lastMovementy1 - startMovementy1;
angle = (float) ((movementY1) / YANGLE_FACTOR);
absoluteAngle = Math.abs(angle);
if (absoluteAngle > MIN_ANGLE_YMOVEMENT
&& absoluteAngle < MAX_ANGLE_YMOVEMENT) {
movement.worldRotationX = angle;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementY=<"
+ movementY1 + ">, angle=<" + absoluteAngle
+ ">");
}
}
}
startMovementx1 = lastMovementx1;
startMovementy1 = lastMovementy1;
startMovementx2 = lastMovementx2;
startMovementy2 = lastMovementy2;
startDistance = lastDistance;
startAngle = lastAngle;
return movement;
} public synchronized void handleMotionEvent(MotionEvent event) {
final int pointerIndex = event.getActionIndex();
final int pointerId = event.getPointerId(pointerIndex); if (DEBUG_MOVEMENT) {
Log.d("MovementHandler", "handleMotionEvent(pointerIndex=<"
+ pointerIndex + ">, pointerId=<" + pointerId
+ ">, event=<" + event.getActionMasked()
+ ">, pointerCount=<" + event.getPointerCount() + ">");
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
_firstPointerId = pointerId;
_secondPointerId = MotionEvent.INVALID_POINTER_ID;
startMovementx1 = event.getX(_firstPointerId);
startMovementy1 = event.getY(_firstPointerId);
lastMovementx1 = startMovementx1;
lastMovementy1 = startMovementy1;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler", "ACTION_DOWN(firstPointerId=<"
+ _firstPointerId + ">(" + startMovementx1 + ","
+ startMovementy1 + ")");
}
break;
}
case MotionEvent.ACTION_UP: {
_firstPointerId = MotionEvent.INVALID_POINTER_ID;
_secondPointerId = MotionEvent.INVALID_POINTER_ID;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler", "ACTION_UP(firstPointerId=<"
+ _firstPointerId + ">)");
}
break;
}
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getPointerCount() == 2) {
_secondPointerId = pointerId;
startMovementx2 = event.getX(_secondPointerId);
startMovementy2 = event.getY(_secondPointerId);
lastMovementx2 = startMovementx2;
lastMovementy2 = startMovementy2;
startDistance = calcDistance();
startAngle = calcAngle();
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_DOWN(secondPointerId=<"
+ _secondPointerId + ">)");
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (pointerId == _firstPointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
_firstPointerId = event.getPointerId(newPointerIndex);
int oldPointerId = _firstPointerId;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_UP(_firstPointerId=<"
+ _firstPointerId + ">, oldPointerId=<"
+ oldPointerId + ">)");
}
startMovementx1 = event.getX(newPointerIndex);
startMovementy1 = event.getY(newPointerIndex);
} else if (pointerId == _secondPointerId) {
if (event.getPointerCount() > 2) {
// This was our active secondpointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 1 ? 2 : 1;
_secondPointerId = event.getPointerId(newPointerIndex);
int oldPointerId = _secondPointerId;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_UP(secondPointerId=<"
+ _secondPointerId
+ ">, oldPointerId=<" + oldPointerId
+ ">)");
}
startMovementx2 = event.getX(newPointerIndex);
startMovementy2 = event.getY(newPointerIndex);
} else {
_secondPointerId = MotionEvent.INVALID_POINTER_ID;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_UP(secondPointerId=<"
+ _secondPointerId + ">)");
}
}
}
break;
case MotionEvent.ACTION_MOVE: {
int firstPointerIndex = event.findPointerIndex(_firstPointerId);
int secondPointerIndex = event.findPointerIndex(_secondPointerId);
if (event.getPointerCount() > 0) {
lastMovementx1 = event.getX(firstPointerIndex);
lastMovementy1 = event.getY(firstPointerIndex);
}
if (event.getPointerCount() > 1) {
lastMovementx2 = event.getX(secondPointerIndex);
lastMovementy2 = event.getY(secondPointerIndex);
lastDistance = calcDistance();
lastAngle = calcAngle();
}
break;
}
}
} private float calcDistance() {
float deltaX = lastMovementx2 - lastMovementx1;
float deltaY = lastMovementy2 - lastMovementy1;
return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
} private float calcAngle() {
return (float) Math.atan2(lastMovementx1 - lastMovementx2,
lastMovementy1 - lastMovementy2);
}
}
/**
*
* @author Ruben.Hesselbaek@beumergroup.com
*
*/
public class Movement { public float worldRotationX;
public float cameraRotationY;
public float cameraMovementX;
public float cameraMovementY;
public float cameraMovementZ; public boolean hasMovement() {
return worldRotationX != 0f || cameraRotationY != 0f
|| cameraMovementX != 0f || cameraMovementY != 0f
|| cameraMovementZ != 0f;
} @Override
public String toString() {
return "Movement (hasMovement=<"+hasMovement()+">, worldRotationX=" + worldRotationX
+ ", cameraRotationY=" + cameraRotationY + ", cameraMovementX="
+ cameraMovementX + ", cameraMovementY=" + cameraMovementY
+ ", cameraMovementZ=" + cameraMovementZ + ")";
} }
@Override
public boolean onTouchEvent(MotionEvent event) { _movementHandler.handleMotionEvent(event);
return true;
} private void moveCamera() {
Movement movement = _movementHandler.getMovement(); if (movement.hasMovement()) {
Camera camera = world.getCamera(); Matrix rot = camera.getBack(); rot.rotateAxis(rot.getYAxis(), -(float) movement.cameraRotationY);
worldObject.rotateX(movement.worldRotationX);
float angle = worldObject.getYAxis().calcAngle(
new SimpleVector(0, 1, 0));
float crossAngle = worldObject.getYAxis().calcCross(
new SimpleVector(0, 1, 0)).x;
if (crossAngle > 0) {
worldObject.rotateX(-angle);
} else {
float delta = (float) (angle - (Math.PI / 2));
if (delta > 0) {
worldObject.rotateX(delta);
}
} camera.moveCamera(Camera.CAMERA_MOVELEFT, movement.cameraMovementX);
camera.moveCamera(Camera.CAMERA_MOVEUP, movement.cameraMovementY);
camera.moveCamera(Camera.CAMERA_MOVEIN, movement.cameraMovementZ);
}
}
MultiTouch camera controls source code的更多相关文章
- Website's Game source code
A Darkroom by doublespeakgames <!DOCTYPE html> <html itemscope itemtype="https://schem ...
- Tips for newbie to read source code
This post is first posted on my WeChat public account: GeekArtT Reading source code is always one bi ...
- 编程等宽字体Source Code Pro(转)
Source Code Pro - 最佳的免费编程字体之一!来自 Adobe 公司的开源等宽字体下载 每一位程序员都有一套自己喜爱的代码编辑器与编程字体,譬如我们之前就推荐过一款"神 ...
- How to build the Robotics Library from source code on Windows
The Robotics Library is an open source C++ library for robot kinematics, motion planning and control ...
- How to build windows azure PowerShell Source Code
Download any version source code of Windows Azure Powershell from https://github.com/Azure/azure-sdk ...
- akka cluster sharding source code 学习 (1/5) 替身模式
为了使一个项目支持集群,自己学习使用了 akka cluster 并在项目中实施了,从此,生活就变得有些痛苦.再配上 apache 做反向代理和负载均衡,debug 起来不要太酸爽.直到现在,我还对 ...
- view class source code with JAD plugin in Eclipse
The default class viewer doesn't decompile the class file so you cannot open and check the source co ...
- Classic Source Code Collected
收藏一些经典的源码,持续更新!!! 1.深度学习框架(Deep Learning Framework). A:Caffe (Convolutional Architecture for Fast Fe ...
- Attach source code to a Netbeans Library Wrapper Module
http://rubenlaguna.com/wp/2008/02/22/attach-source-code-to-a-netbeans-library-wrapper-module/ Attach ...
随机推荐
- [C和指针] rearrange.c
C和指针_程序1.1_重排字符 /* ** 这个程序从标准输入(键盘)中读取输入行并按需求处理后在标准输出(屏幕)中打印, ** 每个输入行的后面一行是该行按需求处理后的输出内容. ** ** 输入的 ...
- 3.bit-map
适用范围:可进行数据的快速查找,判重,删除,一般来说数据范围是int的10倍以下 基本原理及要点:使用bit数组来表示某些元素是否存在,比如8位电话号码 扩展:bloom filter可以看做是对bi ...
- Unity3d Shader开发(三)Pass(Blending )
混合被用于制作透明物体. 当图像被渲染时,所有着色器被执行以后,所有贴图被应用后,像素将被写到屏幕.他们是如何通过Blend命令的控制和已有的图像合并呢? Syntax 语法 Blend Off Tu ...
- ios字符串操作
string的操作应用 NSRange range = [self.general rangeOfString:@"."]; NSString *str = [self.gener ...
- 1003: [ZJOI2006]物流运输trans
spfa+dp; 刚刚开始一直想不通怎么判断他是否换了道: 后来才知道,将那个时间段打包,找出这段时间内的最短路: 真是太奇妙了! #include<cstdio> #include< ...
- 对话 Jessica Hamrick:和 Django 的情缘是我前行的动力
本文出自 Your Django Story 系列,该系列主要突出那些贡献 Django 的女性.点击查看更多,本文系 OneAPM 工程师编译整理. Jess Hamrick 是加州大学伯克利分校心 ...
- 日志分析-Web
http://my.oschina.net/chenguang/blog/376267 http://my.oschina.net/chenguang/blog/371275 http://my.os ...
- asp.net 弹出式日历控件 选择日期 Calendar控件
原文地址:asp.net 弹出式日历控件 选择日期 Calendar控件 作者:逸苡 html代码: <%@ Page Language="C#" CodeFile=&quo ...
- Android imageview显示圆形图片
需要ImageView显示圆形图片做法如下 public static Bitmap toRoundCorner(Bitmap bitmap, float ratio) { System.out.pr ...
- SQL Server中时间段查询和数据类型转换
不知道什么时候对数据独有情种,也许是因为所学专业的缘故,也许是在多年的工作中的亲身经历,无数据,很多事情干不了,数据精度不够,也很多事情干不了,有一次跟一个朋友开玩笑说,如果在写论文的时候,能有一份独 ...