2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 package jmetest.intersection;
35 import java.io.ByteArrayInputStream;
36 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
39 import java.nio.FloatBuffer;
40 import java.util.List;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
44 import com.jme.app.SimpleGame;
45 import com.jme.bounding.BoundingSphere;
46 import com.jme.image.Texture;
47 import com.jme.input.AbsoluteMouse;
48 import com.jme.input.MouseInput;
49 import com.jme.intersection.PickData;
50 import com.jme.intersection.TrianglePickResults;
51 import com.jme.math.Quaternion;
52 import com.jme.math.Ray;
53 import com.jme.math.Vector2f;
54 import com.jme.math.Vector3f;
55 import com.jme.renderer.ColorRGBA;
56 import com.jme.scene.Line;
57 import com.jme.scene.Point;
58 import com.jme.scene.Spatial;
59 import com.jme.scene.TriMesh;
60 import com.jme.scene.state.BlendState;
61 import com.jme.scene.state.TextureState;
62 import com.jme.scene.state.ZBufferState;
63 import com.jme.util.TextureManager;
64 import com.jme.util.export.binary.BinaryImporter;
65 import com.jme.util.geom.BufferUtils;
66 import com.jmex.model.converters.FormatConverter;
67 import com.jmex.model.converters.ObjToJme;
70 * Started Date: Jul 22, 2004 <br>
72 * Demonstrates picking with the mouse.
74 * @author Jack Lindamood
76 public class TestTrianglePick extends SimpleGame {
77 private static final Logger logger = Logger
78 .getLogger(TestTrianglePick.class.getName());
80 // This will be my mouse
83 private Point pointSelection;
87 private Line[] selection;
89 public static void main(String[] args) {
90 TestTrianglePick app = new TestTrianglePick();
91 app.setConfigShowMode(ConfigShowMode.AlwaysShow);
95 protected void simpleInitGame() {
96 // Create a new mouse. Restrict its movements to the display screen.
97 am = new AbsoluteMouse("The Mouse", display.getWidth(), display
100 // Get a picture for my mouse.
101 TextureState ts = display.getRenderer().createTextureState();
102 URL cursorLoc = TestTrianglePick.class.getClassLoader().getResource(
103 "jmetest/data/cursor/cursor1.png");
104 Texture t = TextureManager.loadTexture(cursorLoc, Texture.MinificationFilter.NearestNeighborNoMipMaps,
105 Texture.MagnificationFilter.Bilinear);
107 am.setRenderState(ts);
109 // Make the mouse's background blend with what's already there
110 BlendState as = display.getRenderer().createBlendState();
111 as.setBlendEnabled(true);
112 as.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
113 as.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
114 as.setTestEnabled(true);
115 as.setTestFunction(BlendState.TestFunction.GreaterThan);
116 am.setRenderState(as);
118 // Move the mouse to the middle of the screen to start with
119 am.setLocalTranslation(new Vector3f(display.getWidth() / 2, display
120 .getHeight() / 2, 0));
121 // Assign the mouse to an input handler
122 am.registerWithInputHandler(input);
124 // Create the box in the middle. Give it a bounds
125 URL model = TestTrianglePick.class.getClassLoader().getResource(
126 "jmetest/data/model/maggie.obj");
128 FormatConverter converter = new ObjToJme();
129 converter.setProperty("mtllib", model);
130 ByteArrayOutputStream BO = new ByteArrayOutputStream();
131 converter.convert(model.openStream(), BO);
132 maggie = (Spatial) BinaryImporter.getInstance().load(
133 new ByteArrayInputStream(BO.toByteArray()));
134 // scale rotate and translate to confirm that world transforms are
137 maggie.setLocalScale(.1f);
138 maggie.setLocalTranslation(new Vector3f(3, 1, -5));
139 Quaternion q = new Quaternion();
140 q.fromAngleAxis(0.5f, new Vector3f(0, 1, 0));
141 maggie.setLocalRotation(q);
142 } catch (IOException e) { // Just in case anything happens
143 logger.logp(Level.SEVERE, this.getClass().toString(),
144 "simpleInitGame()", "Exception", e);
148 maggie.setModelBound(new BoundingSphere());
149 maggie.updateModelBound();
151 rootNode.attachChild(maggie);
152 rootNode.attachChild(am);
155 maggie.lockTransforms();
156 results.setCheckDistance(true);
158 pointSelection = new Point("selected triangle", new Vector3f[1], null,
159 new ColorRGBA[1], null);
160 pointSelection.setSolidColor(new ColorRGBA(1, 0, 0, 1));
161 pointSelection.setPointSize(10);
162 pointSelection.setAntialiased(true);
163 ZBufferState zbs = display.getRenderer().createZBufferState();
164 zbs.setFunction(ZBufferState.TestFunction.Always);
165 pointSelection.setRenderState(zbs);
166 pointSelection.setLightCombineMode(Spatial.LightCombineMode.Off);
168 rootNode.attachChild(pointSelection);
171 private void createSelectionTriangles(int number) {
172 clearPreviousSelections();
173 selection = new Line[number];
174 for (int i = 0; i < selection.length; i++) {
175 selection[i] = new Line("selected triangle" + i, new Vector3f[4],
176 null, new ColorRGBA[4], null);
177 selection[i].setSolidColor(new ColorRGBA(0, 1, 0, 1));
178 selection[i].setLineWidth(5);
179 selection[i].setAntialiased(true);
180 selection[i].setMode(Line.Mode.Connected);
182 ZBufferState zbs = display.getRenderer().createZBufferState();
183 zbs.setFunction(ZBufferState.TestFunction.Always);
184 selection[i].setRenderState(zbs);
185 selection[i].setLightCombineMode(Spatial.LightCombineMode.Off);
187 rootNode.attachChild(selection[i]);
190 rootNode.updateGeometricState(0, true);
191 rootNode.updateRenderState();
194 private void clearPreviousSelections() {
195 if (selection != null) {
196 for (Line line : selection) {
197 rootNode.detachChild(line);
202 TrianglePickResults results = new TrianglePickResults() {
204 public void processPick() {
206 // initialize selection triangles, this can go across multiple
210 for (int i = 0; i < getNumber(); i++) {
211 total += getPickData(i).getTargetTris().size();
213 createSelectionTriangles(total);
214 if (getNumber() > 0) {
216 for (int num = 0; num < getNumber(); num++) {
217 PickData pData = getPickData(num);
218 List<Integer> tris = pData.getTargetTris();
219 TriMesh mesh = (TriMesh) pData.getTargetMesh();
221 for (int i = 0; i < tris.size(); i++) {
222 int triIndex = tris.get(i);
223 Vector3f[] vec = new Vector3f[3];
224 mesh.getTriangle(triIndex, vec);
225 FloatBuffer buff = selection[i + previous]
228 for (Vector3f v : vec) {
229 v.multLocal(mesh.getWorldScale());
230 mesh.getWorldRotation().mult(v, v);
231 v.addLocal(mesh.getWorldTranslation());
234 BufferUtils.setInBuffer(vec[0], buff, 0);
235 BufferUtils.setInBuffer(vec[1], buff, 1);
236 BufferUtils.setInBuffer(vec[2], buff, 2);
237 BufferUtils.setInBuffer(vec[0], buff, 3);
239 if (num == 0 && i == 0) {
240 selection[i + previous]
241 .setSolidColor(new ColorRGBA(1, 0, 0, 1));
242 Vector3f loc = new Vector3f();
243 pData.getRay().intersectWhere(vec[0], vec[1],
245 BufferUtils.setInBuffer(loc, pointSelection
246 .getVertexBuffer(), 0);
250 previous = tris.size();
256 // This is called every frame. Do changing of values here.
257 protected void simpleUpdate() {
259 // Is button 0 down? Button 0 is left click
260 if (MouseInput.get().isButtonDown(0)) {
261 Vector2f screenPos = new Vector2f();
262 // Get the position that the mouse is pointing to
263 screenPos.set(am.getHotSpotPosition().x, am.getHotSpotPosition().y);
264 // Get the world location of that X,Y value
265 Vector3f worldCoords = display.getWorldCoordinates(screenPos, 1.0f);
266 // Create a ray starting from the camera, and going in the direction
267 // of the mouse's location
268 final Ray mouseRay = new Ray(cam.getLocation(), worldCoords
269 .subtractLocal(cam.getLocation()));
270 mouseRay.getDirection().normalizeLocal();
273 maggie.calculatePick(mouseRay, results);