001/* 002 * To change this license header, choose License Headers in Project Properties. 003 * To change this template file, choose Tools | Templates 004 * and open the template in the editor. 005 */ 006 007package org.openstreetmap.josm.plugins.streetside.utils; 008 009import java.awt.image.BufferedImage; 010 011import org.openstreetmap.josm.plugins.streetside.cubemap.CubemapUtils; 012import org.openstreetmap.josm.plugins.streetside.cubemap.GraphicsUtils; 013 014import javafx.animation.AnimationTimer; 015import javafx.beans.property.DoubleProperty; 016import javafx.beans.property.SimpleDoubleProperty; 017import javafx.geometry.Rectangle2D; 018import javafx.scene.Group; 019import javafx.scene.PerspectiveCamera; 020import javafx.scene.image.Image; 021import javafx.scene.image.ImageView; 022import javafx.scene.image.WritableImage; 023import javafx.scene.transform.Affine; 024import javafx.scene.transform.Rotate; 025import javafx.scene.transform.Transform; 026 027/** 028 * 029 * @author Dub 030 * @author renerr18 031 */ 032@SuppressWarnings("restriction") 033public class CubemapBox extends Group{ 034 035 public enum CubemapBoxImageType{ 036 MULTIPLE, SINGLE 037 } 038 039 private final Affine affine = new Affine(); 040 041 private final ImageView 042 front = new ImageView(), 043 right = new ImageView(), 044 back = new ImageView(), 045 left = new ImageView(), 046 up = new ImageView(), 047 down = new ImageView(); 048 049 { 050 front.setId(CubemapUtils.CubemapFaces.FRONT.getValue()); 051 right.setId(CubemapUtils.CubemapFaces.RIGHT.getValue()); 052 back.setId(CubemapUtils.CubemapFaces.BACK.getValue()); 053 left.setId(CubemapUtils.CubemapFaces.LEFT.getValue()); 054 up.setId(CubemapUtils.CubemapFaces.UP.getValue()); 055 down .setId(CubemapUtils.CubemapFaces.DOWN.getValue()); 056 057 } 058 private final ImageView[] views = new ImageView[]{ 059 front, 060 right, 061 back, 062 left, 063 up, 064 down 065 }; 066 067 private Image 068 frontImg, rightImg, backImg, leftImg, upImg, downImg, singleImg; 069 070 //private WritableImage convertedImage; 071 072 private final PerspectiveCamera camera; 073 private AnimationTimer timer; 074 private final CubemapBoxImageType imageType; 075 076 public CubemapBox(Image singleImg, double size, PerspectiveCamera camera) { 077 super(); 078 imageType = CubemapBoxImageType.SINGLE; 079 080 this.singleImg = singleImg; 081 this.size.set(size); 082 this.camera = camera; 083 084 getTransforms().add(affine); 085 086 loadImageViews(); 087 088 getChildren().addAll(views); 089 } 090 091 // private constructor to prevent instantiation 092 public CubemapBox(double size, PerspectiveCamera camera) { 093 super(); 094 imageType = CubemapBoxImageType.SINGLE; 095 096 this.size.set(size); 097 this.camera = camera; 098 099 getTransforms().add(affine); 100 101 loadImageViews(); 102 103 getChildren().addAll(views); 104 } 105 106 public CubemapBox(Image frontImg, Image rightImg, Image backImg, Image leftImg, Image upImg, Image downImg, double size, PerspectiveCamera camera) { 107 super(); 108 imageType = CubemapBoxImageType.MULTIPLE; 109 110 this.frontImg = frontImg; 111 this.rightImg = rightImg; 112 this.backImg = backImg; 113 this.leftImg = leftImg; 114 this.upImg = upImg; 115 this.downImg = downImg; 116 this.size.set(size); 117 this.camera = camera; 118 119 loadImageViews(); 120 121 getTransforms().add(affine); 122 123 getChildren().addAll(views); 124 125 startTimer(); 126 } 127 128 public void loadImageViews(){ 129 130 for(ImageView iv : views){ 131 iv.setSmooth(true); 132 iv.setPreserveRatio(true); 133 } 134 135 validateImageType(); 136 } 137 138 private void layoutViews() { 139 140 for(ImageView v : views){ 141 v.setFitWidth(getSize()); 142 v.setFitHeight(getSize()); 143 } 144 145 back.setTranslateX(-0.5 * getSize()); 146 back.setTranslateY(-0.5 * getSize()); 147 back.setTranslateZ(-0.5 * getSize()); 148 149 front.setTranslateX(-0.5 * getSize()); 150 front.setTranslateY(-0.5 * getSize()); 151 front.setTranslateZ(0.5 * getSize()); 152 front.setRotationAxis(Rotate.Z_AXIS); 153 front.setRotate(-180); 154 front.getTransforms().add(new Rotate(180,front.getFitHeight() / 2, 0,0, Rotate.X_AXIS)); 155 front.setTranslateY(front.getTranslateY() - getSize()); 156 157 up.setTranslateX(-0.5 * getSize()); 158 up.setTranslateY(-1 * getSize()); 159 up.setRotationAxis(Rotate.X_AXIS); 160 up.setRotate(-90); 161 162 down.setTranslateX(-0.5 * getSize()); 163 down.setTranslateY(0); 164 down.setRotationAxis(Rotate.X_AXIS); 165 down.setRotate(90); 166 167 left.setTranslateX(-1 * getSize()); 168 left.setTranslateY(-0.5 * getSize()); 169 left.setRotationAxis(Rotate.Y_AXIS); 170 left.setRotate(90); 171 172 right.setTranslateX(0); 173 right.setTranslateY(-0.5 * getSize()); 174 right.setRotationAxis(Rotate.Y_AXIS); 175 right.setRotate(-90); 176 177 } 178 /** 179 * for single image creates viewports and sets all views(image) to singleImg 180 * for multiple... sets images per view. 181 */ 182 private void validateImageType(){ 183 switch(imageType){ 184 case SINGLE: 185 loadSingleImageViewports(); 186 break; 187 case MULTIPLE: 188 // TODO: implement single and double properly @rrh 189 setMultipleImages(); 190 break; 191 } 192 } 193 // TODO: change layout - Streetside cubemaps are structured as follows: @rrh 194 /** 195 * 01-front, 02-right, 03-back, 10-left, 11-up, 12-down 196 * 197 * ____ ____ ____ ____ ____ ____ 198 * |fwd |rght|back|left|top |bot | 199 * |____|____|____|____|____|____| 200 * 201 */ 202 203 private void loadSingleImageViewports(){ 204 layoutViews(); 205 double width = singleImg.getWidth(), 206 height = singleImg.getHeight(); 207 208 //simple check to see if cells will be square 209 if(width / 4 != height / 3){ 210 throw new UnsupportedOperationException("Image does not comply with size constraints"); 211 } 212 double cellSize = singleImg.getWidth() - singleImg.getHeight(); 213 214 recalculateSize(cellSize); 215 216 double 217 topx = cellSize, topy = 0, 218 219 botx = cellSize, boty = cellSize * 2, 220 221 leftx = 0, lefty = cellSize, 222 223 rightx = cellSize * 2, righty = cellSize, 224 225 fwdx = cellSize, fwdy = cellSize, 226 227 backx = cellSize * 3, backy = cellSize; 228 229 //add top padding x+, y+, width-, height 230 up.setViewport(new Rectangle2D(topx , topy , cellSize, cellSize )); 231 232 //add left padding x, y+, width, height- 233 left.setViewport(new Rectangle2D(leftx , lefty , cellSize - 1, cellSize - 1)); 234 235 //add front padding x+, y+, width-, height 236 front.setViewport(new Rectangle2D(fwdx , fwdy, cellSize , cellSize)); 237 238 //add right padding x, y+, width, height- 239 right.setViewport(new Rectangle2D(rightx, righty , cellSize , cellSize )); 240 241 //add back padding x, y+, width, height- 242 back.setViewport(new Rectangle2D(backx + 1, backy - 1, cellSize - 1, cellSize - 1)); 243 244 //add bottom padding x+, y, width-, height- 245 down.setViewport(new Rectangle2D(botx, boty, cellSize , cellSize)); 246 247 for(ImageView v : views){ 248 v.setImage(singleImg); 249 } 250 } 251 252 253 254 private void recalculateSize(double cell){ 255 double factor = Math.floor(getSize() / cell); 256 setSize(cell * factor); 257 } 258 259 public synchronized void setImage(BufferedImage img, int position) { 260 views[position].setImage(GraphicsUtils.convertBufferedImage2JavaFXImage(img)); 261 262 } 263 264 265 private void setMultipleImages() { 266 GraphicsUtils.PlatformHelper.run(() -> { 267 layoutViews(); 268 front.setImage(frontImg); 269 right.setImage(rightImg); 270 back.setImage(backImg); 271 left.setImage(leftImg); 272 up.setImage(upImg); 273 down.setImage(downImg); 274 275 }); 276 } 277 278 public void startTimer(){ 279 timer = new AnimationTimer() { 280 @Override 281 public void handle(long now) { 282 Transform ct = (camera != null) ? camera.getLocalToSceneTransform() : null; 283 if(ct != null){ 284 affine.setTx(ct.getTx()); 285 affine.setTy(ct.getTy()); 286 affine.setTz(ct.getTz()); 287 } 288 } 289 }; 290 timer.start(); 291 } 292 293 /* 294 Properties 295 */ 296 private final DoubleProperty size = new SimpleDoubleProperty(){ 297 @Override 298 protected void invalidated() { 299 switch(imageType){ 300 case SINGLE: 301 layoutViews(); 302 break; 303 case MULTIPLE: 304 break; 305 } 306 307 } 308 }; 309 310 public final double getSize() { 311 return size.get(); 312 } 313 314 public final void setSize(double value) { 315 size.set(value); 316 } 317 318 public DoubleProperty sizeProperty() { 319 return size; 320 } 321 322 public ImageView[] getViews() { 323 return views; 324 } 325}