import javafx.application.Application; import javafx.embed.swing.SwingFXUtils; import javafx.scene.*; import javafx.scene.Cursor; import javafx.scene.image.Image; import javafx.scene.input.MouseButton; import javafx.scene.paint.Color; import javafx.scene.paint.PhongMaterial; import javafx.scene.shape.Box; import javafx.scene.shape.Cylinder; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; import javafx.stage.Stage; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public class CubeComposer extends Application { //IMPORTANT: 16 units = 1 block private static final double BLOCK_UNIT = 16; private static final int PIXEL_PER_UNIT = 50; //used for increasing the texture quality private static final double MAJOR_LINE_THICCNESS = 0.1; private static final double MINOR_LINE_THICCNESS = 0.03; private static final double GRID_SIZE = 6; private static final double SENSITIVITY = 1; private static final double ZOOM_SENSITIVITY = 0.2; private static final int ZOOM_MIN = 80; private static final int ZOOM_MAX = 2000; private final double sceneWidth = 1280; private final double sceneHeight = 720; private static final String texture = "res/wood.png"; private double cursorLocX, cursorLocY; private Translate zoom = new Translate(0, -16, -200); private double angleX = 0; private double angleY = 0; private Rotate rotateX = new Rotate(0, 0, -16, 0, Rotate.X_AXIS); private Rotate rotateY = new Rotate(0, 0, -16, 0, Rotate.Y_AXIS); private Rotate rotateZ = new Rotate(0, 0, -16, 0, Rotate.Z_AXIS); private final Group root = new Group(); private PerspectiveCamera camera; private static Image increaseResolution(BufferedImage image, double sizex, double sizey) { BufferedImage i = new BufferedImage((int) sizex * PIXEL_PER_UNIT, (int) sizey * PIXEL_PER_UNIT, BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) i.getGraphics(); g.drawImage(image, 0, 0, i.getWidth(), i.getHeight(), null); return SwingFXUtils.toFXImage(i, null); } @Override public void start(Stage stage) { BufferedImage woodTexture = null; try { woodTexture = ImageIO.read(new File(texture)); } catch (IOException ignored) { } //wooden box Box wood = new Box(BLOCK_UNIT, BLOCK_UNIT, BLOCK_UNIT); PhongMaterial woodenBox = new PhongMaterial(); woodenBox.setDiffuseMap(increaseResolution(woodTexture, BLOCK_UNIT, BLOCK_UNIT)); wood.setMaterial(woodenBox); wood.getTransforms().addAll(new Translate(0, 8.1, 0)); root.getChildren().add(wood); //gridlines PhongMaterial majorGridlineMaterial = new PhongMaterial(Color.GRAY); PhongMaterial minorGridlineMaterial = new PhongMaterial(Color.GRAY); for (double x = -GRID_SIZE / 2 + 1 / BLOCK_UNIT; GRID_SIZE / 2 > x; x += 1 / BLOCK_UNIT) { if ((x + 0.5) % 1 == 0) { Cylinder c = new Cylinder(MAJOR_LINE_THICCNESS, GRID_SIZE * BLOCK_UNIT); c.setMaterial(majorGridlineMaterial); c.getTransforms().addAll(new Translate(x * BLOCK_UNIT, 0, 0), new Rotate(90, Rotate.X_AXIS)); root.getChildren().add(c); } else { Cylinder c = new Cylinder(MINOR_LINE_THICCNESS, GRID_SIZE * BLOCK_UNIT); c.setMaterial(minorGridlineMaterial); c.getTransforms().addAll(new Translate(x * BLOCK_UNIT, 0, 0), new Rotate(90, Rotate.X_AXIS)); root.getChildren().add(c); } } for (double y = -GRID_SIZE / 2 + 1 / BLOCK_UNIT; GRID_SIZE / 2 > y; y += 1 / BLOCK_UNIT) { if ((y + 0.5) % 1 == 0) { Cylinder c = new Cylinder(MAJOR_LINE_THICCNESS, GRID_SIZE * BLOCK_UNIT); c.setMaterial(majorGridlineMaterial); c.getTransforms().addAll(new Translate(0, 0, y * BLOCK_UNIT), new Rotate(90, Rotate.Z_AXIS)); root.getChildren().add(c); } else { Cylinder c = new Cylinder(MINOR_LINE_THICCNESS, GRID_SIZE * BLOCK_UNIT); c.setMaterial(minorGridlineMaterial); c.getTransforms().addAll(new Translate(0, 0, y * BLOCK_UNIT), new Rotate(90, Rotate.Z_AXIS)); root.getChildren().add(c); } } //scene creation Scene scene = new Scene(root, sceneWidth, sceneHeight, true, SceneAntialiasing.BALANCED); scene.setFill(new Color(0.4, 0.9, 0.9, 1)); //creating the camera camera = new PerspectiveCamera(true); camera.setVerticalFieldOfView(false); camera.setNearClip(0.1); camera.setFarClip(100000.0); camera.getTransforms().addAll(rotateX, rotateY, rotateZ, zoom); //lightning root.getChildren().add(new AmbientLight(Color.WHITE)); //add camera to the scene scene.setCamera(camera); //controls scene.setOnMousePressed(event -> { if (event.getButton() == MouseButton.MIDDLE) { scene.setCursor(Cursor.MOVE); cursorLocX = event.getScreenX(); cursorLocY = event.getScreenY(); } }); scene.setOnMouseReleased(event -> { if (event.getButton() == MouseButton.MIDDLE) scene.setCursor(Cursor.DEFAULT); }); scene.setOnMouseDragged(event -> { if (event.getButton() == MouseButton.MIDDLE) { rotateCameraBy(event.getScreenY() - cursorLocY, event.getScreenX() - cursorLocX); cursorLocX = event.getScreenX(); cursorLocY = event.getScreenY(); } }); scene.setOnScroll(event -> zoomCameraBy(event.getDeltaY())); //showing the scene stage.setTitle("3D Editor"); stage.setScene(scene); stage.show(); } private void zoomCameraBy(double diff) { if (diff == 0) return; if (-(zoom.getZ() + diff) >= ZOOM_MIN && -(zoom.getZ() + diff) <= ZOOM_MAX) zoom.setZ(zoom.getZ() + diff * ZOOM_SENSITIVITY); } private void rotateCameraBy(double diffX, double diffY) { if (diffX == 0 && diffY == 0) return; angleX -= diffX / sceneWidth * 360 * SENSITIVITY; angleY += diffY / sceneHeight * 360 * SENSITIVITY; rotateY.setAngle(angleY); //rotateX.setAngle(angleX); rotateX.setAngle(angleX*Math.cos(Math.toRadians(angleY))); rotateZ.setAngle(angleX*Math.sin(Math.toRadians(angleY))); } }