/**
* Skybox.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.scene;
import javafx.animation.AnimationTimer;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
/**
*
* @author Dub
*/
public class Skybox extends Group{
public enum SkyboxImageType{
MULTIPLE, SINGLE
}
private final Affine affine = new Affine();
private final ImageView
top = new ImageView(),
bottom= new ImageView(),
left = new ImageView(),
right = new ImageView(),
back = new ImageView(),
front = new ImageView();
{
top.setId("top ");
bottom.setId("bottom ");
left.setId("left ");
right.setId("right ");
back.setId("back ");
front.setId("front ");
}
private final ImageView[] views = new ImageView[]{
top,
left,
back,
right,
front,
bottom
};
private Image
topImg, bottomImg, leftImg, rightImg, frontImg, backImg, singleImg;
private WritableImage convertedImage;
private final PerspectiveCamera camera;
private AnimationTimer timer;
private final SkyboxImageType imageType;
public Skybox(Image singleImg, double size, PerspectiveCamera camera) {
super();
this.imageType = SkyboxImageType.SINGLE;
this.singleImg = singleImg;
this.size.set(size);
this.camera = camera;
getTransforms().add(affine);
loadImageViews();
getChildren().addAll(views);
}
public Skybox(Image topImg, Image bottomImg, Image leftImg, Image rightImg, Image frontImg, Image backImg, double size, PerspectiveCamera camera) {
super();
this.imageType = SkyboxImageType.MULTIPLE;
this.topImg = topImg;
this.bottomImg = bottomImg;
this.leftImg = leftImg;
this.rightImg = rightImg;
this.frontImg = frontImg;
this.backImg = backImg;
this.size.set(size);
this.camera = camera;
loadImageViews();
getTransforms().add(affine);
getChildren().addAll(views);
startTimer();
}
private void loadImageViews(){
for(ImageView iv : views){
iv.setSmooth(true);
iv.setPreserveRatio(true);
}
validateImageType();
}
private void layoutViews() {
for(ImageView v : views){
v.setFitWidth(getSize());
v.setFitHeight(getSize());
}
back.setTranslateX(-0.5 * getSize());
back.setTranslateY(-0.5 * getSize());
back.setTranslateZ(-0.5 * getSize());
front.setTranslateX(-0.5 * getSize());
front.setTranslateY(-0.5 * getSize());
front.setTranslateZ(0.5 * getSize());
front.setRotationAxis(Rotate.Z_AXIS);
front.setRotate(-180);
front.getTransforms().add(new Rotate(180,front.getFitHeight() / 2, 0,0, Rotate.X_AXIS));
front.setTranslateY(front.getTranslateY() - getSize());
top.setTranslateX(-0.5 * getSize());
top.setTranslateY(-1 * getSize());
top.setRotationAxis(Rotate.X_AXIS);
top.setRotate(-90);
bottom.setTranslateX(-0.5 * getSize());
bottom.setTranslateY(0);
bottom.setRotationAxis(Rotate.X_AXIS);
bottom.setRotate(90);
left.setTranslateX(-1 * getSize());
left.setTranslateY(-0.5 * getSize());
left.setRotationAxis(Rotate.Y_AXIS);
left.setRotate(90);
right.setTranslateX(0);
right.setTranslateY(-0.5 * getSize());
right.setRotationAxis(Rotate.Y_AXIS);
right.setRotate(-90);
}
/**
* for single image creates viewports and sets all views(image) to singleImg
* for multiple... sets images per view.
*/
private void validateImageType(){
switch(imageType){
case SINGLE:
loadSingleImageViewports();
break;
case MULTIPLE:
setMultipleImages();
break;
}
}
/**
* this will layout the viewports to this style pattern
* ____
* |top |
* ____|____|____ ____
* |left|fwd |rght|back|
* |____|____|____|____|
* |bot |
* |____|
*
*/
private void loadSingleImageViewports(){
layoutViews();
double width = singleImg.getWidth(),
height = singleImg.getHeight();
//simple chack to see if cells will be square
if(width / 4 != height / 3){
throw new UnsupportedOperationException("Image does not comply with size constraints");
}
double cellSize = singleImg.getWidth() - singleImg.getHeight();
recalculateSize(cellSize);
double
topx = cellSize, topy = 0,
botx = cellSize, boty = cellSize * 2,
leftx = 0, lefty = cellSize,
rightx = cellSize * 2, righty = cellSize,
fwdx = cellSize, fwdy = cellSize,
backx = cellSize * 3, backy = cellSize;
//add top padding x+, y+, width-, height
top.setViewport(new Rectangle2D(topx , topy , cellSize, cellSize ));
//add left padding x, y+, width, height-
left.setViewport(new Rectangle2D(leftx , lefty , cellSize - 1, cellSize - 1));
//add front padding x+, y+, width-, height
back.setViewport(new Rectangle2D(fwdx , fwdy, cellSize , cellSize));
//add right padding x, y+, width, height-
right.setViewport(new Rectangle2D(rightx, righty , cellSize , cellSize ));
//add back padding x, y+, width, height-
front.setViewport(new Rectangle2D(backx + 1, backy - 1, cellSize - 1, cellSize - 1));
//add bottom padding x+, y, width-, height-
bottom.setViewport(new Rectangle2D(botx, boty, cellSize , cellSize));
for(ImageView v : views){
v.setImage(singleImg);
//System.out.println(v.getId() + v.getViewport() + cellSize);
}
}
private void recalculateSize(double cell){
double factor = Math.floor(getSize() / cell);
setSize(cell * factor);
}
private void setMultipleImages() {
layoutViews();
back.setImage(frontImg);
front.setImage(backImg);
top.setImage(topImg);
bottom.setImage(bottomImg);
left.setImage(leftImg);
right.setImage(rightImg);
}
private void startTimer(){
timer = new AnimationTimer() {
@Override
public void handle(long now) {
Transform ct = (camera != null) ? camera.getLocalToSceneTransform() : null;
if(ct != null){
affine.setTx(ct.getTx());
affine.setTy(ct.getTy());
affine.setTz(ct.getTz());
}
}
};
timer.start();
}
/*
Properties
*/
private final DoubleProperty size = new SimpleDoubleProperty(){
@Override
protected void invalidated() {
switch(imageType){
case SINGLE:
layoutViews();
break;
case MULTIPLE:
break;
}
}
};
public final double getSize() {
return size.get();
}
public final void setSize(double value) {
size.set(value);
}
public DoubleProperty sizeProperty() {
return size;
}
}