Skip to content

Commit 6e618b8

Browse files
committed
Tap to select object
1 parent d2d466b commit 6e618b8

File tree

8 files changed

+168
-73
lines changed

8 files changed

+168
-73
lines changed

AndroidManifest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="org.andresoviedo.dddmodel"
4-
android:versionCode="3"
5-
android:versionName="1.0" >
4+
android:versionCode="4"
5+
android:versionName="1.2.0" >
66

77
<uses-sdk
88
android:minSdkVersion="8"

README.md

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ It is basically an android application with a 3D renderer (OpenGL ES 2.0) that c
88
The purpose of this application is to learn and share how to draw using OpenGL language
99

1010

11-
News (30/03/2016)
11+
News (06/04/2016)
1212
=================
1313

14-
* Released app on android market :) https://play.google.com/store/apps/details?id=org.andresoviedo.dddmodel
15-
* Light rendering
14+
* Implemented selection of objects (click on Demo and touch the different objects)
15+
* Light rendering implemented
1616
* Toggle textures & lights
17+
* Released app on android market :) https://play.google.com/store/apps/details?id=org.andresoviedo.dddmodel
18+
1719

1820
About
1921
=====
@@ -56,13 +58,15 @@ Features
5658
- display of normals
5759
- display of bounding box
5860
- scaling, rotation
59-
- object picking
60-
- primitive collision detection (not yet!)
61-
- animation of sprites (not yet!)
61+
- object selection
6262
- touch support!
63-
* rotation gesture to rotate
64-
* pinch gesture to zoom
63+
- tap to select object
64+
- drag to move camera
65+
- rotate to rotate camera
66+
- pinch & spread to zoom in/out the camera
6567
- moving of objects (not yet!)
68+
- primitive collision detection (not yet!)
69+
- animation of sprites (not yet!)
6670

6771

6872
Try it
@@ -99,4 +103,20 @@ Final Notes
99103
===========
100104

101105
You are free to use this program while you keep this file and the authoring comments in the code.
102-
Any comments and suggestions are welcome.
106+
Any comments and suggestions are welcome.
107+
108+
109+
ChangeLog
110+
=========
111+
112+
(f) fixed, (i) improved, (n) new feature
113+
114+
- 1.2.0 (06/04/2016)
115+
- (n) Implemented selection of objects
116+
117+
- 1.1.0 (30/03/2016)
118+
- (n) Implemented lighting & toggle textures & lights
119+
- (i) Refactoring of 3DObjectImpl
120+
121+
- 1.0.0 (27/03/2016)
122+
- (n) First release in Google Play Android Market

android-3DModel.apk

416 Bytes
Binary file not shown.

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

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

3+
import org.andresoviedo.app.model3D.model.Object3DData;
4+
import org.andresoviedo.app.model3D.services.SceneLoader;
35
import org.andresoviedo.app.model3D.view.ModelRenderer;
6+
import org.andresoviedo.app.model3D.view.ModelSurfaceView;
7+
import org.andresoviedo.app.util.math.Math3DUtils;
48

59
import android.graphics.PointF;
6-
import android.opengl.GLSurfaceView;
710
import android.opengl.GLU;
811
import android.opengl.Matrix;
12+
import android.os.SystemClock;
913
import android.util.FloatMath;
1014
import android.util.Log;
1115
import android.view.MotionEvent;
@@ -20,7 +24,7 @@ public class TouchController {
2024
private static final int TOUCH_STATUS_ROTATING_CAMERA = 4;
2125
private static final int TOUCH_STATUS_MOVING_WORLD = 5;
2226

23-
private GLSurfaceView view;
27+
private final ModelSurfaceView view;
2428
private final ModelRenderer mRenderer;
2529

2630
int pointerCount = 0;
@@ -41,17 +45,14 @@ public class TouchController {
4145
float rotation = 0;
4246
int currentSquare = Integer.MIN_VALUE;
4347

44-
float[] hit1 = null;
45-
float[] hit2 = null;
46-
float[] hit3 = null;
47-
float[] hit4 = null;
48-
float hitSquare = -1;
49-
float hitTriangle = -1;
5048
boolean isOneFixedAndOneMoving = false;
5149
boolean fingersAreClosing = false;
5250
boolean isRotating = false;
5351

5452
boolean gestureChanged = false;
53+
private boolean moving = false;
54+
private boolean simpleTouch = false;
55+
private long lastActionTime;
5556
private int touchDelay = -2;
5657
private int touchStatus = -1;
5758

@@ -64,7 +65,7 @@ public class TouchController {
6465
float[] rotationVector = new float[4];
6566
private float previousRotationSquare;
6667

67-
public TouchController(GLSurfaceView view, ModelRenderer renderer) {
68+
public TouchController(ModelSurfaceView view, ModelRenderer renderer) {
6869
super();
6970
this.view = view;
7071
this.mRenderer = renderer;
@@ -77,18 +78,33 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
7778

7879
switch (motionEvent.getActionMasked()) {
7980
case MotionEvent.ACTION_UP:
80-
case MotionEvent.ACTION_DOWN:
8181
case MotionEvent.ACTION_CANCEL:
8282
case MotionEvent.ACTION_POINTER_UP:
83-
case MotionEvent.ACTION_POINTER_DOWN:
83+
case MotionEvent.ACTION_HOVER_EXIT:
8484
case MotionEvent.ACTION_OUTSIDE:
85+
// this to handle "1 simple touch"
86+
if (lastActionTime > SystemClock.uptimeMillis() - 250) {
87+
simpleTouch = true;
88+
} else {
89+
gestureChanged = true;
90+
touchDelay = 0;
91+
lastActionTime = SystemClock.uptimeMillis();
92+
simpleTouch = false;
93+
}
94+
moving = false;
95+
break;
96+
case MotionEvent.ACTION_DOWN:
97+
case MotionEvent.ACTION_POINTER_DOWN:
8598
case MotionEvent.ACTION_HOVER_ENTER:
86-
case MotionEvent.ACTION_HOVER_EXIT:
87-
Log.d("Touch", "Gesture changed...");
99+
Log.d(TAG, "Gesture changed...");
88100
gestureChanged = true;
89101
touchDelay = 0;
102+
lastActionTime = SystemClock.uptimeMillis();
103+
simpleTouch = false;
90104
break;
91105
case MotionEvent.ACTION_MOVE:
106+
moving = true;
107+
simpleTouch = false;
92108
touchDelay++;
93109
break;
94110
default:
@@ -108,9 +124,6 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
108124
}
109125
dx1 = x1 - previousX1;
110126
dy1 = y1 - previousY1;
111-
hit1 = unproject(x1, y1, 0);
112-
hit2 = unproject(x1, y1, 1);
113-
// Log.d("Ray", "Ray2---> x:" + xyzw2[0] + " y:" + xyzw2[1] + " z:" + xyzw2[2] + " w(" + xyzw2[3] + ")");
114127
} else if (pointerCount == 2) {
115128
x1 = motionEvent.getX(0);
116129
y1 = motionEvent.getY(0);
@@ -152,12 +165,6 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
152165
currentPress1 = motionEvent.getPressure(0);
153166
currentPress2 = motionEvent.getPressure(1);
154167
rotation = 0;
155-
hit1 = unproject(x1, y1, 0);
156-
hit2 = unproject(x1, y1, 1);
157-
hit3 = unproject(x2, y2, 0);
158-
hit4 = unproject(x2, y2, 1);
159-
// wzSquare = hit(xyzw3, xyzw2, mRenderer.getmSquare().getPosition());
160-
// wzTriangle = hit(xyzw1, xyzw2, mRenderer.getmTriangle().getPosition());
161168
rotation = TouchScreen.getRotation360(motionEvent);
162169
currentSquare = TouchScreen.getSquare(motionEvent);
163170
if (currentSquare == 1 && previousRotationSquare == 4) {
@@ -173,12 +180,17 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
173180
&& rotationVector[2] != 0;
174181
}
175182

183+
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);
189+
}
190+
176191
if (touchDelay > 1) {
177192
// INFO: Procesar gesto
178193
if (pointerCount == 1 && currentPress1 > 4.0f) {
179-
// TODO: enable this
180-
// hitSquare = hit(hit1, hit2, mRenderer.getScene().getSquare1().getPosition());
181-
// hitTriangle = hit(hit1, hit2, mRenderer.getScene().getSquare1().getPosition());
182194
} else if (pointerCount == 1) {
183195
touchStatus = TOUCH_STATUS_MOVING_WORLD;
184196
// Log.i("Touch", "Moving World '" + dx1 + "','" + dy1 + "'...");
@@ -278,7 +290,7 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
278290

279291
if (gestureChanged && touchDelay > 1) {
280292
gestureChanged = false;
281-
Log.i("Fin", "Fin");
293+
Log.v(TAG, "Fin");
282294
}
283295

284296
view.requestRender();
@@ -287,6 +299,41 @@ public synchronized boolean onTouchEvent(MotionEvent motionEvent) {
287299

288300
}
289301

302+
/**
303+
* Get the nearest object intersecting the specified ray and selects it
304+
*
305+
* @param nearPoint
306+
* the near point in world coordinates
307+
* @param farPoint
308+
* the far point in world coordinates
309+
*/
310+
private void selectObjectImpl(float[] nearPoint, float[] farPoint) {
311+
SceneLoader scene = view.getModelActivity().getScene();
312+
if (scene == null) {
313+
return;
314+
}
315+
Object3DData objectToSelect = null;
316+
float objectToSelectDistance = Integer.MAX_VALUE;
317+
for (Object3DData obj : scene.getObjects()) {
318+
float distance = Math3DUtils.calculateDistanceOfIntersection(nearPoint, farPoint, obj.getPosition(), 1f);
319+
if (distance != -1) {
320+
Log.d(TAG, "Hit object " + obj.getId() + " at distance " + distance);
321+
if (distance < objectToSelectDistance) {
322+
objectToSelectDistance = distance;
323+
objectToSelect = obj;
324+
}
325+
}
326+
}
327+
if (objectToSelect != null) {
328+
Log.i(TAG, "Selected object " + objectToSelect.getId() + " at distance " + objectToSelectDistance);
329+
if (scene.getSelectedObject() == objectToSelect) {
330+
scene.setSelectedObject(null);
331+
} else {
332+
scene.setSelectedObject(objectToSelect);
333+
}
334+
}
335+
}
336+
290337
public float[] unproject(float rx, float ry, float rz) {
291338
float[] xyzw = { 0, 0, 0, 0 };
292339

@@ -303,33 +350,6 @@ public float[] unproject(float rx, float ry, float rz) {
303350
xyzw[3] = 1;
304351
return xyzw;
305352
}
306-
307-
public float hit(float[] xyzw, float[] xyzw2, float[] position) {
308-
float zPrecition = 1000f;
309-
310-
float xDif = (xyzw2[0] - xyzw[0]) / zPrecition;
311-
float yDif = (xyzw2[1] - xyzw[1]) / zPrecition;
312-
float zDif = (xyzw2[2] - xyzw[2]) / zPrecition;
313-
314-
// @formatter:off
315-
for (int i = 0; i < zPrecition; i++) {
316-
// Log.d("Hit cube", "HIT");
317-
double objWidth = 1;
318-
double objHalfWidth = objWidth/2;
319-
float xIncr = xDif * i;
320-
if (( xyzw[0] + xIncr) > position[0] - objHalfWidth
321-
&& (xyzw[0] + xIncr) < position[0] + objHalfWidth
322-
&& (xyzw[1] + (yDif * i)) > position[1] - objHalfWidth
323-
&& (xyzw[1] + (yDif * i)) < position[1] + objHalfWidth
324-
&& (xyzw[2] + (zDif * i)) > position[2] - objHalfWidth
325-
&& (xyzw[2] + (zDif * i)) < position[2] + objHalfWidth) {
326-
Log.w("Hit", "HIT: i["+i+"] wz["+(xyzw[2] + (zDif * i))+"]");
327-
return xyzw[2] + (zDif * i);
328-
}
329-
}
330-
// @formatter:on
331-
return -1;
332-
}
333353
}
334354

335355
class TouchScreen {

src/org/andresoviedo/app/model3D/services/ExampleSceneLoader.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ protected void onPreExecute() {
4747
@Override
4848
protected Void doInBackground(Void... params) {
4949
try {
50+
// 3D Axis
51+
Object3DData axis = Object3DBuilder.buildAxis().setId("axis");
52+
axis.setColor(new float[] { 1.0f, 0, 0, 1.0f });
53+
addObject(axis);
54+
5055
// test cube made of arrays
5156
Object3DData obj10 = Object3DBuilder.buildCubeV1();
5257
obj10.setColor(new float[] { 1f, 0f, 0f, 0.5f });
@@ -58,6 +63,7 @@ protected Void doInBackground(Void... params) {
5863
obj11.setColor(new float[] { 1f, 1f, 0f, 0.5f });
5964
obj11.setPosition(new float[] { 0f, 2f, 0f });
6065
obj11.centerAndScaleAndExplode(1.0f, 1.5f);
66+
obj11.setId(obj11.getId() + "_exploded");
6167
addObject(obj11);
6268

6369
// test cube made of wires (I explode it to see the faces better)

src/org/andresoviedo/app/model3D/services/SceneLoader.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ public class SceneLoader {
2525
* Parent component
2626
*/
2727
protected final ModelActivity parent;
28-
/**
29-
* The 3D axis
30-
*/
31-
private Object3DData axis;
3228
/**
3329
* List of data objects containing info for building the opengl objects
3430
*/
@@ -61,16 +57,16 @@ public class SceneLoader {
6157
* Light position
6258
*/
6359
private float[] lightPos = new float[] { 0, 0, 3, 1 };
60+
/**
61+
* Object selected by the user
62+
*/
63+
private Object3DData selectedObject = null;
6464

6565
public SceneLoader(ModelActivity main) {
6666
this.parent = main;
6767
}
6868

6969
public void init() {
70-
// Draw Axis
71-
axis = Object3DBuilder.buildAxis().setId("axis");
72-
axis.setColor(new float[] { 1.0f, 0, 0, 1.0f });
73-
addObject(axis);
7470

7571
// Load object
7672
if (parent.getParamFile() != null || parent.getParamAssetDir() != null) {
@@ -178,4 +174,12 @@ public boolean isDrawLighting() {
178174
return drawLighting;
179175
}
180176

177+
public Object3DData getSelectedObject() {
178+
return selectedObject;
179+
}
180+
181+
public void setSelectedObject(Object3DData selectedObject) {
182+
this.selectedObject = selectedObject;
183+
}
184+
181185
}

src/org/andresoviedo/app/model3D/view/ModelRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ public void onDrawFrame(GL10 unused) {
186186
}
187187

188188
// Draw bounding box
189-
if (scene.isDrawBoundingBox()) {
189+
if (scene.isDrawBoundingBox() || scene.getSelectedObject() == objData) {
190190
Object3DData boundingBoxData = boundingBoxes.get(objData);
191191
if (boundingBoxData == null || changed) {
192192
boundingBoxData = Object3DBuilder.buildBoundingBox(objData);

0 commit comments

Comments
 (0)