• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.tests.bullet;
18 
19 import java.nio.FloatBuffer;
20 import java.nio.ShortBuffer;
21 
22 import com.badlogic.gdx.Gdx;
23 import com.badlogic.gdx.graphics.Color;
24 import com.badlogic.gdx.graphics.Mesh;
25 import com.badlogic.gdx.graphics.VertexAttributes;
26 import com.badlogic.gdx.graphics.g3d.Model;
27 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
28 import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
29 import com.badlogic.gdx.math.Vector3;
30 import com.badlogic.gdx.math.collision.Ray;
31 import com.badlogic.gdx.physics.bullet.collision.btBvhTriangleMeshShape;
32 import com.badlogic.gdx.physics.bullet.collision.btTriangleIndexVertexArray;
33 import com.badlogic.gdx.physics.bullet.collision.btTriangleRaycastCallback;
34 import com.badlogic.gdx.physics.bullet.collision.btTriangleRaycastCallback.EFlags;
35 import com.badlogic.gdx.physics.bullet.linearmath.btVector3;
36 
37 /** @author jsjolund */
38 public class TriangleRaycastTest extends BaseBulletTest {
39 
40 	private class MyTriangleRaycastCallback extends btTriangleRaycastCallback {
41 
42 		public Vector3 hitNormalLocal = new Vector3();
43 		public float hitFraction = 1;
44 		public int partId = -1;
45 		public int triangleIndex = -1;
46 
47 		private btVector3 tmpSetFrom = new btVector3();
48 		private btVector3 tmpSetTo = new btVector3();
49 
MyTriangleRaycastCallback(Vector3 from, Vector3 to)50 		public MyTriangleRaycastCallback (Vector3 from, Vector3 to) {
51 			super(from, to);
52 		}
53 
clearReport()54 		public void clearReport () {
55 			hitNormalLocal.setZero();
56 			hitFraction = 1;
57 			partId = -1;
58 			triangleIndex = -1;
59 		}
60 
61 		@Override
setHitFraction(float hitFraction)62 		public void setHitFraction (float hitFraction) {
63 			super.setHitFraction(hitFraction);
64 			this.hitFraction = hitFraction;
65 		}
66 
67 		@Override
reportHit(Vector3 hitNormalLocal, float hitFraction, int partId, int triangleIndex)68 		public float reportHit (Vector3 hitNormalLocal, float hitFraction, int partId, int triangleIndex) {
69 			// The hit with lowest hitFraction is closest to the ray origin.
70 			// We need to find the lowest hitFraction since the super class does not handle it for us.
71 			if (hitFraction < this.hitFraction) {
72 				this.hitNormalLocal.set(hitNormalLocal);
73 				this.hitFraction = hitFraction;
74 				this.partId = partId;
75 				this.triangleIndex = triangleIndex;
76 			}
77 			return hitFraction;
78 		}
79 
setFrom(Vector3 value)80 		public void setFrom (Vector3 value) {
81 			tmpSetFrom.setValue(value.x, value.y, value.z);
82 			super.setFrom(tmpSetFrom);
83 		}
84 
setTo(Vector3 value)85 		public void setTo (Vector3 value) {
86 			tmpSetTo.setValue(value.x, value.y, value.z);
87 			super.setTo(tmpSetTo);
88 		}
89 
90 		@Override
dispose()91 		public void dispose () {
92 			tmpSetFrom.dispose();
93 			tmpSetTo.dispose();
94 			super.dispose();
95 		}
96 	}
97 
98 	private Model model;
99 	private btBvhTriangleMeshShape triangleShape;
100 	private MyTriangleRaycastCallback triangleRaycastCallback;
101 	private Vector3[] selectedTriangleVertices = {new Vector3(), new Vector3(), new Vector3()};
102 	private ShapeRenderer shapeRenderer;
103 	private Vector3 rayFrom = new Vector3();
104 	private Vector3 rayTo = new Vector3();
105 
106 	@Override
create()107 	public void create () {
108 		super.create();
109 		instructions = "Tap a triangle to ray cast\nLong press to toggle debug mode\nSwipe for next test\nCtrl+drag to rotate\nScroll to zoom";
110 
111 		shapeRenderer = new ShapeRenderer();
112 
113 		model = objLoader.loadModel(Gdx.files.internal("data/scene.obj"));
114 		model.materials.get(0).clear();
115 		model.materials.get(0).set(ColorAttribute.createDiffuse(Color.WHITE), ColorAttribute.createSpecular(Color.WHITE));
116 
117 		// Only indexed BvhTriangleMeshShape can be used for triangle picking.
118 		btTriangleIndexVertexArray vertexArray = new btTriangleIndexVertexArray(model.meshParts);
119 		triangleShape = new btBvhTriangleMeshShape(vertexArray, true);
120 
121 		triangleRaycastCallback = new MyTriangleRaycastCallback(Vector3.Zero, Vector3.Zero);
122 		// Ignore intersection with mesh backfaces.
123 		triangleRaycastCallback.setFlags(EFlags.kF_FilterBackfaces);
124 
125 		world.addConstructor("scene", new BulletConstructor(model, 0, triangleShape));
126 		world.add("scene", 0, 0, 0);
127 
128 		disposables.add(model);
129 		disposables.add(triangleRaycastCallback);
130 		disposables.add(triangleShape);
131 		disposables.add(vertexArray);
132 		disposables.add(shapeRenderer);
133 
134 	}
135 
136 	@Override
render()137 	public void render () {
138 		super.render();
139 		Gdx.gl.glLineWidth(5);
140 		shapeRenderer.setProjectionMatrix(camera.combined);
141 		shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
142 		shapeRenderer.setColor(1, 0, 0, 1f);
143 		shapeRenderer.line(selectedTriangleVertices[0], selectedTriangleVertices[1]);
144 		shapeRenderer.line(selectedTriangleVertices[1], selectedTriangleVertices[2]);
145 		shapeRenderer.line(selectedTriangleVertices[2], selectedTriangleVertices[0]);
146 		shapeRenderer.end();
147 		Gdx.gl.glLineWidth(1);
148 	}
149 
150 	@Override
tap(float screenX, float screenY, int count, int button)151 	public boolean tap (float screenX, float screenY, int count, int button) {
152 		Ray ray = camera.getPickRay(screenX, screenY);
153 		rayFrom.set(ray.origin);
154 		rayTo.set(ray.direction).scl(100).add(rayFrom);
155 
156 		// Clean the callback object for reuse.
157 		triangleRaycastCallback.setHitFraction(1);
158 		triangleRaycastCallback.clearReport();
159 		triangleRaycastCallback.setFrom(rayFrom);
160 		triangleRaycastCallback.setTo(rayTo);
161 
162 		// Ray casting is performed directly on the collision shape.
163 		// The callback specifies the intersected MeshPart as well as triangle.
164 		triangleShape.performRaycast(triangleRaycastCallback, rayFrom, rayTo);
165 		int currentTriangleIndex = triangleRaycastCallback.triangleIndex;
166 		int currentPartId = triangleRaycastCallback.partId;
167 
168 		if (currentTriangleIndex == -1 || currentPartId == -1) {
169 			// No intersection was found.
170 			return false;
171 		}
172 
173 		// Get the position coordinates of the vertices belonging to intersected triangle.
174 		Mesh mesh = model.meshParts.get(currentPartId).mesh;
175 		FloatBuffer verticesBuffer = mesh.getVerticesBuffer();
176 		ShortBuffer indicesBuffer = mesh.getIndicesBuffer();
177 
178 		int posOffset = mesh.getVertexAttributes().findByUsage(VertexAttributes.Usage.Position).offset / 4;
179 		int vertexSize = mesh.getVertexSize() / 4;
180 		int currentTriangleFirstVertexIndex = currentTriangleIndex * 3;
181 
182 		// Store the three vertices belonging to the selected triangle.
183 		for (int i = 0; i < 3; i++) {
184 			int currentVertexIndex = indicesBuffer.get(currentTriangleFirstVertexIndex + i);
185 			int j = currentVertexIndex * vertexSize + posOffset;
186 			float x = verticesBuffer.get(j++);
187 			float y = verticesBuffer.get(j++);
188 			float z = verticesBuffer.get(j);
189 			selectedTriangleVertices[i].set(x, y, z);
190 		}
191 		return true;
192 	}
193 
194 }
195