/** * RayShooting.java * * Copyright (c) 2013-2016, F(X)yz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of F(X)yz, any associated website, nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL F(X)yz BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.fxyz3d.samples.utilities; import com.sun.javafx.scene.CameraHelper; import javafx.animation.Interpolator; import javafx.animation.PauseTransition; import javafx.animation.Transition; import javafx.geometry.Point3D; import javafx.scene.AmbientLight; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.PointLight; import javafx.scene.input.KeyCode; import javafx.scene.paint.Color; import javafx.scene.paint.PhongMaterial; import javafx.scene.shape.CullFace; import javafx.scene.shape.DrawMode; import javafx.scene.shape.Shape3D; import javafx.scene.shape.Sphere; import javafx.util.Duration; import org.fxyz3d.geometry.Ray; import org.fxyz3d.samples.shapes.ShapeBaseSample; /** * A simple app Showing the newly added Ray class. * <br><p> * Clicking on the Scene will spawn a Sphere at that position (origin of * Ray)<br> * Mouse buttons target different nodes Targets are breifly highlighted if an * intersection occurs. * <br><br> * RayTest in org.fxyz3d.tests package has reference on TriangleMesh intersections * * </p><br> * * @author Jason Pollastrini aka jdub1581 */ public class RayShooting extends ShapeBaseSample { public static void main(String[] args){launch(args);} private final PhongMaterial red = new PhongMaterial(Color.ORCHID), blue = new PhongMaterial(Color.BLUEVIOLET), highlight = new PhongMaterial(Color.LIME.brighter()); //========================================================================== private final AmbientLight rayLight = new AmbientLight(); protected PointLight light, light2, light3; private boolean fireRay = true; protected Sphere target1, target2; @Override protected void addMeshAndListeners() { camera.setTranslateZ(-5000); //First person shooter keyboard movement subScene.setOnKeyPressed(ke -> { // add a flag so we can still move the camera if (ke.getCode().equals(KeyCode.CONTROL)) { fireRay = false; } }); subScene.setOnKeyReleased(ke -> { // release flag if (ke.getCode().equals(KeyCode.CONTROL)) { fireRay = true; } }); subScene.setOnMousePressed(e -> { if (fireRay) { Point3D o = CameraHelper.pickProjectPlane(camera, e.getX(), e.getY()); if (e.isPrimaryButtonDown()) { // set Target and Direction Point3D t = Point3D.ZERO.add(target2.getTranslateX(), target2.getTranslateY(), target2.getTranslateZ()), d = t.subtract(o); //Build the Ray Ray r = new Ray(o, d); double dist = t.distance(o); // If ray intersects node, spawn and animate if (target2.getBoundsInParent().contains(r.project(dist))) { animateRayTo(r, target2, Duration.seconds(2)); e.consume(); } e.consume(); } // repeat for other target as well else if (e.isSecondaryButtonDown()) { Point3D tgt = Point3D.ZERO.add(target1.getTranslateX(), target1.getTranslateY(), target1.getTranslateZ()), dir = tgt.subtract(o); Ray r = new Ray(o, dir); double dist = tgt.distance(o); if (target1.getBoundsInParent().contains(r.project(dist))) { animateRayTo(r, target1, Duration.seconds(2)); e.consume(); } e.consume(); } }e.consume(); }); //*/ } /** * * @param r The Ray that holds the info * @param tx to x * @param ty to y * @param tz to z * @param dps distance per step to move ray * @param time length of animation */ private void animateRayTo(final Ray r, final Shape3D target, final Duration time) { final Transition t = new Transition() { protected Ray ray; protected Sphere s; protected double dist; { this.ray = r; this.s = new Sphere(5); s.setTranslateX((ray.getOrigin()).getX()); s.setTranslateY((ray.getOrigin()).getY()); s.setTranslateZ((ray.getOrigin()).getZ()); s.setMaterial(highlight); rayLight.getScope().add(s); this.dist = ray.getOrigin().distance( Point3D.ZERO.add(target.getTranslateX(), target.getTranslateY(), target.getTranslateZ()) ); setCycleDuration(time); this.setInterpolator(Interpolator.LINEAR); this.setOnFinished(e -> { if (target.getBoundsInParent().contains(ray.getPosition())) { if (target.getBoundsInLocal().contains(target.parentToLocal(ray.getPosition()))) { target.setMaterial(highlight); } PauseTransition t = new PauseTransition(Duration.millis(150)); t.setOnFinished(pe -> { reset(); group.getChildren().removeAll(s); s = null; }); t.playFromStart(); } }); group.getChildren().add(s); } @Override protected void interpolate(double frac) { // frac-> 0.0 - 1.0 // project ray ray.project(dist * frac); // set the sphere to ray position s.setTranslateX(ray.getPosition().getX()); s.setTranslateY(ray.getPosition().getY()); s.setTranslateZ(ray.getPosition().getZ()); } }; t.playFromStart(); } // resets materisl on targets private void reset() { ((Shape3D)((Group)model).getChildren().get(0)).setMaterial(red); ((Shape3D)((Group)model).getChildren().get(1)).setMaterial(blue); } @Override protected void createMesh() { // add camera so it doesn't affect all nodes rayLight.getScope().add(camera); light = new PointLight(Color.GAINSBORO); light.setTranslateX(-300); light.setTranslateY(300); light.setTranslateZ(-2000); light2 = new PointLight(Color.ALICEBLUE); light2.setTranslateX(300); light2.setTranslateY(-300); light2.setTranslateZ(2000); light3 = new PointLight(Color.SPRINGGREEN); light3.setTranslateY(-2000); //create a target target1 = new Sphere(180); target1.setId("t1"); target1.setDrawMode(DrawMode.LINE); target1.setCullFace(CullFace.NONE); target1.setTranslateX(300); target1.setTranslateY(300); target1.setTranslateZ(2500); target1.setMaterial(red); // create another target target2 = new Sphere(150); target2.setId("t2"); target2.setDrawMode(DrawMode.LINE); target2.setCullFace(CullFace.NONE); target2.setTranslateX(200); target2.setTranslateY(-200); target2.setTranslateZ(-500); target2.setMaterial(blue); model = new Group(target1, target2, light, light2, light3, rayLight); } @Override protected Node buildControlPanel() { return null; } }