/**
* Billboarding.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 java.util.Arrays;
import java.util.Random;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Pos;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.SubScene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import org.fxyz3d.controls.CameraViewControl;
import org.fxyz3d.controls.ComboBoxControl;
import org.fxyz3d.controls.ControlPanel;
import org.fxyz3d.controls.SectionLabel;
import org.fxyz3d.controls.factory.ControlFactory;
import org.fxyz3d.samples.shapes.ShapeBaseSample;
import org.fxyz3d.scene.BillboardNode;
import org.fxyz3d.scene.BillboardNode.BillboardMode;
import org.fxyz3d.scene.CameraView;
import org.fxyz3d.shapes.primitives.TorusMesh;
/**
*
* @author Dub
*/
public class Billboarding extends ShapeBaseSample<BillBoard> {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
//**************************************************************************
private final BooleanProperty useCameraView = new SimpleBooleanProperty(this, "CameraView Enabled", true);
private final BooleanProperty active = new SimpleBooleanProperty(this, "Billboarding Active"); //Flag for toggling behavior
private final ObjectProperty<BillboardMode> mode = new SimpleObjectProperty<BillboardMode>(this, "BillBoard Mode", BillboardMode.SPHERICAL) {
@Override
protected void invalidated() {
if (model != null) {
model.setBillboardMode(getValue());
System.out.println("mode changed");
}
}
};
protected CameraView cameraView;
private void appendSubScene() {
camera.setTranslateZ(-2000);
initFirstPersonControls(subScene);
//Make a bunch of semi random Tori and stuff : from torustest
final Group torusGroup = new Group();
for (int i = 0; i < 10; i++) {
Random r = new Random();
//A lot of magic numbers in here that just artificially constrain the math
float randomRadius = (float) ((r.nextFloat() * 300) + 50);
float randomTubeRadius = (float) ((r.nextFloat() * 100) + 1);
int randomTubeDivisions = (int) ((r.nextFloat() * 64) + 1);
int randomRadiusDivisions = (int) ((r.nextFloat() * 64) + 1);
Color randomColor = new Color(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble());
boolean ambientRandom = r.nextBoolean();
boolean fillRandom = r.nextBoolean();
TorusMesh torus = new TorusMesh(randomTubeDivisions, randomRadiusDivisions, randomRadius, randomTubeRadius);
torus.setMaterial(new PhongMaterial(randomColor));
double translationX = Math.random() * 1024 * 1.95;
if (Math.random() >= 0.5) {
translationX *= -1;
}
double translationY = Math.random() * 1024 * 1.95;
if (Math.random() >= 0.5) {
translationY *= -1;
}
double translationZ = Math.random() * 1024 * 1.95;
if (Math.random() >= 0.5) {
translationZ *= -1;
}
Translate translate = new Translate(translationX, translationY, translationZ);
Rotate rotX = new Rotate(Math.random() * 360, Rotate.X_AXIS);
Rotate rotY = new Rotate(Math.random() * 360, Rotate.Y_AXIS);
Rotate rotZ = new Rotate(Math.random() * 360, Rotate.Z_AXIS);
torus.getTransforms().addAll(translate, rotX, rotY, rotZ);
//torus.getTransforms().add(translate);
torusGroup.getChildren().add(torus);
}
group.getChildren().add(torusGroup);
}
/*
private void createCameraView() {
cameraView = new CameraView(subScene);
cameraView.setFitWidth(250);
cameraView.setFitHeight(200);
cameraView.setFirstPersonNavigationEabled(true);
cameraView.setFocusTraversable(true);
cameraView.getCamera().setTranslateZ(-2500);
cameraView.getCamera().setTranslateX(500);
StackPane.setAlignment(cameraView, Pos.BOTTOM_RIGHT);
StackPane.setMargin(cameraView, new Insets(20));
mainPane.getChildren().add(cameraView);
cameraView.startViewing();
}
*/
private void initFirstPersonControls(SubScene scene) {
//make sure Subscene handles KeyEvents
scene.setOnMouseEntered(e -> {
scene.requestFocus();
});
//First person shooter keyboard movement
scene.setOnKeyPressed(event -> {
double change = 10.0;
//Add shift modifier to simulate "Running Speed"
if (event.isShiftDown()) {
change = 50.0;
}
//What key did the user press?
KeyCode keycode = event.getCode();
//Step 2c: Add Zoom controls
if (keycode == KeyCode.W) {
camera.setTranslateZ(camera.getTranslateZ() + change);
}
if (keycode == KeyCode.S) {
camera.setTranslateZ(camera.getTranslateZ() - change);
}
//Step 2d: Add Strafe controls
if (keycode == KeyCode.A) {
camera.setTranslateX(camera.getTranslateX() - change);
}
if (keycode == KeyCode.D) {
camera.setTranslateX(camera.getTranslateX() + change);
}
});
}
@Override
protected void createMesh() {
model = new BillBoard(camera);
model.setBillboardMode(mode.get());
Platform.runLater(() -> {
//createCameraView();
appendSubScene();
});
}
@Override
protected void addMeshAndListeners() {
model.activeProperty().bind(active);
model.parentProperty().addListener(l->{
if(model.getParent() != null){
final CameraViewControl camView = new CameraViewControl(useCameraView, subScene, mainPane);
camView.visibleProperty().bind(useCameraView);
StackPane.setAlignment(camView, Pos.BOTTOM_RIGHT);
}
});
}
@Override
protected Node buildControlPanel() {
ControlPanel panel = ControlFactory.buildSingleListControlPanel();
panel.addToRoot(
new SectionLabel("BillBoarding Properties"),
ControlFactory.buildCheckBoxControl(active),
new ComboBoxControl<>("Billboarding Mode", mode, Arrays.asList(BillboardMode.values()), false),
new SectionLabel("CameraView"),
ControlFactory.buildCheckBoxControl(useCameraView),
ControlFactory.buildCheckBoxControl(useSkybox)
);
return panel;
}
}
//****************** BillBoard ***********************
class BillBoard extends BillboardNode<ImageView> {
private final Node other;
private final ImageView view;
public BillBoard(Node other) {
super();
this.other = other;
view = new ImageView(new Image("http://overshoot.tv/sites/overshoot.tv/files/Oak_Tree_0.png"));
view.setFitWidth(200);
view.setPreserveRatio(true);
view.setSmooth(true);
setDepthTest(DepthTest.ENABLE);
getChildren().add(view);
}
@Override
protected ImageView getBillboardNode() {
return view;
}
@Override
protected Node getTarget() {
return other;
}
}