Skip to content

Commit fafc71e

Browse files
committed
Improved collision detection algorithm
1 parent b09dd16 commit fafc71e

File tree

12 files changed

+542
-441
lines changed

12 files changed

+542
-441
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ The purpose of this application is to learn and share how to draw using OpenGL l
1212
* Collada format (DAE): https://en.wikipedia.org/wiki/COLLADA
1313

1414

15-
News (17/12/2017)
15+
News (21/12/2017)
1616
=================
1717

18+
* Improved collision detection algorithm (ray-aabb)
1819
* Support for collada files with skeletal animations :)
1920
* Fixed #28: Load texture feature is now available
2021

@@ -122,6 +123,10 @@ ChangeLog
122123

123124
(f) fixed, (i) improved, (n) new feature
124125

126+
- 2.0.3 (21/12/2017)
127+
- (i) Improved collision detection algorithm (ray-aabb) for selecting objects
128+
- (i) BoundingBox code cleanup
129+
125130
- 2.0.2 (17/12/2017)
126131
- (f) Collada XML parser is now android's XmlPullParser
127132
- (f) Animation engine frame times improved
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package org.andresoviedo.app.model3D.collision;
2+
3+
import android.opengl.GLU;
4+
import android.util.Log;
5+
6+
import org.andresoviedo.app.model3D.entities.BoundingBox;
7+
import org.andresoviedo.app.model3D.model.Object3DData;
8+
import org.andresoviedo.app.model3D.view.ModelRenderer;
9+
import org.andresoviedo.app.util.math.Math3DUtils;
10+
11+
import java.util.List;
12+
13+
/**
14+
* Class that encapsulates all the logic for the collision detection algorithm.
15+
*
16+
* @author andresoviedo
17+
*/
18+
public class CollisionDetection {
19+
20+
/**
21+
* Get the nearest object intersected by the specified window coordinates
22+
*
23+
* @param objects the list of objects to test
24+
* @param mRenderer the model renderer to unproject window coordinates to world coordinates
25+
* @param windowX the window x coordinate
26+
* @param windowY the window y coordinate
27+
* @return the nearest object intersected by the specified coordinates or null
28+
*/
29+
public static Object3DData getIntersection(List<Object3DData> objects, ModelRenderer mRenderer, float windowX, float windowY) {
30+
float[] nearHit = unproject(mRenderer, windowX, windowY, 0);
31+
float[] farHit = unproject(mRenderer, windowX, windowY, 1);
32+
return getIntersection(objects, nearHit, farHit);
33+
}
34+
35+
/**
36+
* Get the nearest object intersected by the specified ray or null if no object is intersected
37+
*
38+
* @param objects the list of objects to test
39+
* @param p1 the ray start point
40+
* @param p2 the ray end point
41+
* @return the object intersected by the specified ray
42+
*/
43+
public static Object3DData getIntersection(List<Object3DData> objects, float[] p1, float[] p2) {
44+
float[] direction = Math3DUtils.substract(p2, p1);
45+
Math3DUtils.normalize(direction);
46+
float min = Float.MAX_VALUE;
47+
Object3DData ret = null;
48+
for (Object3DData obj : objects) {
49+
if (obj.getId().startsWith("Line")) continue;
50+
BoundingBox box = obj.getBoundingBox();
51+
float[] intersection = getBoxIntersection(p1, direction, box);
52+
if (intersection[0] > 0 && intersection[0] < intersection[1] && intersection[0] < min) {
53+
min = intersection[0];
54+
ret = obj;
55+
}
56+
}
57+
if (ret != null) {
58+
Log.i("CollisionDetection", "Collision detected '" + ret.getId() + "' distance: " + min);
59+
}
60+
return ret;
61+
}
62+
63+
/**
64+
* Get the entry and exit point of the ray intersecting the nearest object or null if no object is intersected
65+
*
66+
* @param objects list of objects to test
67+
* @param p1 ray start point
68+
* @param p2 ray end point
69+
* @return the entry and exit point of the ray intersecting the nearest object
70+
*/
71+
public static float[] getIntersectionPoint(List<Object3DData> objects, float[] p1, float[] p2) {
72+
float[] direction = Math3DUtils.substract(p2, p1);
73+
Math3DUtils.normalize(direction);
74+
float min = Float.MAX_VALUE;
75+
float[] intersection2 = null;
76+
Object3DData ret = null;
77+
for (Object3DData obj : objects) {
78+
if (obj.getId().startsWith("Line")) continue;
79+
BoundingBox box = obj.getBoundingBox();
80+
float[] intersection = getBoxIntersection(p1, direction, box);
81+
if (intersection[0] > 0 && intersection[0] < intersection[1] && intersection[0] < min) {
82+
min = intersection[0];
83+
ret = obj;
84+
intersection2 = intersection;
85+
}
86+
}
87+
if (ret != null) {
88+
Log.i("CollisionDetection", "Collision detected '" + ret.getId() + "' distance: " + min);
89+
return new float[]{p1[0] + direction[0] * min, p1[1] + direction[1] * min, p1[2] + direction[2] * min};
90+
}
91+
return null;
92+
}
93+
94+
/**
95+
* Return true if the specified ray intersects the bounding box
96+
*
97+
* @param origin origin of the ray
98+
* @param dir direction of the ray
99+
* @param b bounding box
100+
* @return true if the specified ray intersects the bounding box, false otherwise
101+
*/
102+
public static boolean isBoxIntersection(float[] origin, float[] dir, BoundingBox b) {
103+
float[] intersection = getBoxIntersection(origin, dir, b);
104+
return intersection[0] > 0 && intersection[0] < intersection[1];
105+
}
106+
107+
/**
108+
* Get the intersection points of the near and far plane for the specified ray and bounding box
109+
*
110+
* @param origin the ray origin
111+
* @param dir the ray direction
112+
* @param b the bounding box
113+
* @return the intersection points of the near and far plane
114+
*/
115+
public static float[] getBoxIntersection(float[] origin, float[] dir, BoundingBox b) {
116+
float[] tMin = Math3DUtils.divide(Math3DUtils.substract(b.getCurrentMin(), origin), dir);
117+
float[] tMax = Math3DUtils.divide(Math3DUtils.substract(b.getCurrentMax(), origin), dir);
118+
float[] t1 = Math3DUtils.min(tMin, tMax);
119+
float[] t2 = Math3DUtils.max(tMin, tMax);
120+
float tNear = Math.max(Math.max(t1[0], t1[1]), t1[2]);
121+
float tFar = Math.min(Math.min(t2[0], t2[1]), t2[2]);
122+
return new float[]{tNear, tFar};
123+
}
124+
125+
/**
126+
* Get the corresponding near and far vertex for the specified window coordinates
127+
*
128+
* @param mRenderer
129+
* @param rx
130+
* @param ry
131+
* @param rz
132+
* @return the corresponding near and far vertex for the specified window coordinates
133+
*/
134+
public static float[] unproject(ModelRenderer mRenderer, float rx, float ry, float rz) {
135+
float[] xyzw = {0, 0, 0, 0};
136+
ry = (float) mRenderer.getHeight() - ry;
137+
int[] viewport = {0, 0, mRenderer.getWidth(), mRenderer.getHeight()};
138+
GLU.gluUnProject(rx, ry, rz, mRenderer.getModelViewMatrix(), 0, mRenderer.getModelProjectionMatrix(), 0,
139+
viewport, 0, xyzw, 0);
140+
xyzw[0] /= xyzw[3];
141+
xyzw[1] /= xyzw[3];
142+
xyzw[2] /= xyzw[3];
143+
xyzw[3] = 1;
144+
return xyzw;
145+
}
146+
}

app/src/main/java/org/andresoviedo/app/model3D/controller/TouchController.java

Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package org.andresoviedo.app.model3D.controller;
22

3+
import org.andresoviedo.app.model3D.collision.CollisionDetection;
4+
import org.andresoviedo.app.model3D.model.Object3DBuilder;
35
import org.andresoviedo.app.model3D.model.Object3DData;
46
import org.andresoviedo.app.model3D.services.SceneLoader;
57
import org.andresoviedo.app.model3D.view.ModelRenderer;
68
import org.andresoviedo.app.model3D.view.ModelSurfaceView;
7-
import org.andresoviedo.app.util.math.Math3DUtils;
89

910
import android.graphics.PointF;
1011
import android.opengl.GLU;
@@ -16,6 +17,8 @@
1617
import android.view.View;
1718
import android.widget.ImageView;
1819

20+
import java.util.Arrays;
21+
1922
public class TouchController {
2023

2124
private static final String TAG = TouchController.class.getName();
@@ -181,11 +184,10 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
181184
}
182185

183186
if (pointerCount == 1 && simpleTouch) {
184-
// calculate the world coordinates where the user is clicking (near plane and far plane)
185-
float[] hit1 = unproject(x1, y1, 0);
186-
float[] hit2 = unproject(x1, y1, 1);
187-
// check if the ray intersect any of our objects and select the nearer
188-
selectObjectImpl(hit1, hit2);
187+
SceneLoader scene = view.getModelActivity().getScene();
188+
if (scene != null) {
189+
scene.processTouch(x1,y1);
190+
}
189191
}
190192

191193

@@ -300,59 +302,6 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
300302
return true;
301303

302304
}
303-
304-
/**
305-
* Get the nearest object intersecting the specified ray and selects it
306-
*
307-
* @param nearPoint
308-
* the near point in world coordinates
309-
* @param farPoint
310-
* the far point in world coordinates
311-
*/
312-
private void selectObjectImpl(float[] nearPoint, float[] farPoint) {
313-
SceneLoader scene = view.getModelActivity().getScene();
314-
if (scene == null) {
315-
return;
316-
}
317-
Object3DData objectToSelect = null;
318-
float objectToSelectDistance = Integer.MAX_VALUE;
319-
for (Object3DData obj : scene.getObjects()) {
320-
Log.i(TAG, "Testing object " + obj.getId());
321-
float distance = Math3DUtils.calculateDistanceOfIntersection(nearPoint, farPoint, obj.getBoundingBox().getCenter(), 1f);
322-
if (distance != -1) {
323-
Log.i(TAG, "Hit object " + obj.getId() + " at distance " + distance);
324-
if (distance < objectToSelectDistance) {
325-
objectToSelectDistance = distance;
326-
objectToSelect = obj;
327-
}
328-
}
329-
}
330-
if (objectToSelect != null) {
331-
Log.i(TAG, "Selected object " + objectToSelect.getId() + " at distance " + objectToSelectDistance);
332-
if (scene.getSelectedObject() == objectToSelect) {
333-
scene.setSelectedObject(null);
334-
} else {
335-
scene.setSelectedObject(objectToSelect);
336-
}
337-
}
338-
}
339-
340-
public float[] unproject(float rx, float ry, float rz) {
341-
float[] xyzw = { 0, 0, 0, 0 };
342-
343-
ry = (float) mRenderer.getHeight() - ry;
344-
345-
int[] viewport = { 0, 0, mRenderer.getWidth(), mRenderer.getHeight() };
346-
347-
GLU.gluUnProject(rx, ry, rz, mRenderer.getModelViewMatrix(), 0, mRenderer.getModelProjectionMatrix(), 0,
348-
viewport, 0, xyzw, 0);
349-
350-
xyzw[0] /= xyzw[3];
351-
xyzw[1] /= xyzw[3];
352-
xyzw[2] /= xyzw[3];
353-
xyzw[3] = 1;
354-
return xyzw;
355-
}
356305
}
357306

358307
class TouchScreen {

0 commit comments

Comments
 (0)