3D Domino in Java Swing

How to Create a 3D Domino in Java Netbeans

How to Create a 3D Domino in Java Netbeans


In this Java Tutorial we will see How To Create a 3D Dominp Piece that rotates, floats, and responds to user input In Java Using Netbeans.

What We Are Gonna Use In This Project:

- Java Programming Language.
- NetBeans Editor.





Project Source Code:



/**
 *
 * @author 1BestCsharp
 */
public class Domino3D extends JFrame{

    // Rotation angles for the 3D domino
    private double currentRotationX = -30;   // Current X-axis rotation in degrees
    private double currentRotationY = 45;    // Current Y-axis rotation in degrees
        
    // Target angles that the domino will animate towards
    private double targetRotationX = -30;    // Target X-axis rotation
    private double targetRotationY = 45;     // Target Y-axis rotation
    
    // Animation properties
    private final double ROTATION_SPEED = 0.15;  // Speed of rotation animation
    private boolean isAnimating = false;         // Flag to track if animation is in progress
    
    // Mouse handling properties
    private boolean isDragging = false;      // Flag to track if user is dragging the domino
    private Point previousPoint;             // Previous mouse position for drag calculations
        
    // Domino properties
    private int topValue = 6;                // Number on top half of domino (1-6)
    private int bottomValue = 3;             // Number on bottom half of domino (1-6)
    private Color dominoColor = new Color(250, 250, 250);  // Main domino color
    private Color dotColor = new Color(20, 20, 20);        // Color of dots on domino
    private Color dividerColor = new Color(40, 40, 40);    // Color of center dividing line
        
    // Domino dimensions
    private static final double DOMINO_WIDTH = 120;    // Width of domino
    private static final double DOMINO_HEIGHT = 240;   // Height of domino
    private static final double DOMINO_DEPTH = 25;     // Depth of domino
        
    // Floating animation properties
    private Timer animationTimer;            // Timer for animation updates
    private double floatOffset = 0;          // Current vertical offset for floating effect
    private boolean floatingUp = true;       // Direction of floating animation

    // Drawing panel
    private DominoPanel dominoPanel;         // Panel where the domino is drawn
    
     /**
     * Constructor: Sets up the application window and initializes components
     */
     public Domino3D(){
         
            // Set up the main window
            setTitle("Modern Domino");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setLayout(new BorderLayout());
        
            // Create the panel where domino will be drawn
            dominoPanel = new DominoPanel();
            dominoPanel.setPreferredSize(new Dimension(800, 600));
            dominoPanel.setBackground(new Color(15, 15, 15));
            add(dominoPanel, BorderLayout.CENTER);

            // Create and add control buttons
            JPanel controlPanel = createControlPanel();
            add(controlPanel, BorderLayout.SOUTH);

            // Set up mouse event handlers
            setupMouseListeners();

            // Start animation timer
            startAnimation();

            // Finalize window setup
            pack();
            setLocationRelativeTo(null);  // Center on screen
     }
    
     
     
    /**
     * Creates the control panel with buttons
     */
    private JPanel createControlPanel() {
        // Create panel with dark background and padding
        JPanel panel = new JPanel();
        panel.setBackground(new Color(30, 30, 30));
        panel.setBorder(new EmptyBorder(15, 15, 15, 15));
        panel.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 0));
        
        // Create buttons for rotation
        JButton rotateXButton = createControlButton("Rotate X");
        JButton rotateYButton = createControlButton("Rotate Y");
        
        // Add action to rotate around X axis
        rotateXButton.addActionListener(e -> {
            targetRotationX += 90;  // Rotate 90 degrees around X axis
            isAnimating = true;
        });
        
        // Add action to rotate around Y axis
        rotateYButton.addActionListener(e -> {
            targetRotationY += 90;  // Rotate 90 degrees around Y axis
            isAnimating = true;
        });
        
        // Add buttons to panel
        panel.add(rotateXButton);
        panel.add(rotateYButton);
        
        return panel;
        
    }
     
     
     
    /**
     * Creates a styled button with hover effects
     */
    private JButton createControlButton(String text) {
        // Create button with text
        JButton button = new JButton(text);
        
        // Style the button
        button.setForeground(Color.WHITE);
        button.setBackground(new Color(60, 60, 60));
        button.setFocusPainted(false);
        button.setBorderPainted(false);
        button.setFont(new Font("SansSerif", Font.BOLD, 14));
        button.setPreferredSize(new Dimension(120, 40));
        
        // Add hover and click effects
        button.addMouseListener(new MouseAdapter() {
            // Change appearance when mouse enters button area
            @Override
            public void mouseEntered(MouseEvent e) {
                button.setBackground(new Color(80, 80, 80));
                button.setCursor(new Cursor(Cursor.HAND_CURSOR));
            }
            
            // Restore appearance when mouse leaves button area
            @Override
            public void mouseExited(MouseEvent e) {
                button.setBackground(new Color(60, 60, 60));
                button.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
             
            // Pressed
            @Override
            public void mousePressed(MouseEvent e) {
                button.setBackground(new Color(100, 100, 100));
            }
            
            // Released
            @Override
            public void mouseReleased(MouseEvent e) {
                button.setBackground(new Color(80, 80, 80));
            }
            
        });
        
        return button;
        
    }
     
    
        
    /**
     * Sets up mouse listeners for rotating the domino with drag actions
     */
    private void setupMouseListeners() {
        // Handle mouse press and release
        dominoPanel.addMouseListener(new MouseAdapter() {
            // Start tracking drag when mouse is pressed
            @Override
            public void mousePressed(MouseEvent e) {
                isDragging = true;
                previousPoint = e.getPoint();  // Store initial mouse position
                dominoPanel.setCursor(new Cursor(Cursor.MOVE_CURSOR));
            }
            
            // Stop tracking drag when mouse is released
            @Override
            public void mouseReleased(MouseEvent e) {
                isDragging = false;
                dominoPanel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
            
        });
        
        // Handle mouse drag motion
        dominoPanel.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                if (isDragging) {
                    
                    Point currentPoint = e.getPoint();
                    
                    // Calculate how far mouse has moved since last position
                    double deltaX = currentPoint.x - previousPoint.x;
                    double deltaY = currentPoint.y - previousPoint.y;
                    
                    // Apply rotations based on mouse movement
                    targetRotationY += deltaX * 0.5;  // Horizontal motion rotates Y
                    targetRotationX += deltaY * 0.5;  // Vertical motion rotates X
                    
                    previousPoint = currentPoint;  // Update previous position
                    dominoPanel.repaint();  // Redraw with new rotation
                }
                
            }
            
        });
        
    }
    
    
    /**
     * Starts the animation timer for continuous updates
     */
    private void startAnimation() {
        // Create timer that updates every 16ms
        animationTimer = new Timer(16, e -> {
            // Handle floating animation (gentle up and down movement)
            if (floatingUp) {
                floatOffset += 0.15;  // Move up
                if (floatOffset >= 8) floatingUp = false;  // Change direction at peak
            } else {
                floatOffset -= 0.15;  // Move down
                if (floatOffset <= -8) floatingUp = true;  // Change direction at bottom
            }
            
            // Handle rotation animation
            boolean needsUpdate = false;
            
            // Smoothly animate X rotation toward target
            if (Math.abs(targetRotationX - currentRotationX) > 0.1) {
                currentRotationX += (targetRotationX - currentRotationX) * ROTATION_SPEED;
                needsUpdate = true;
            } else {
                currentRotationX = targetRotationX;  // Snap to exact target when close
            }
            
            // Smoothly animate Y rotation toward target
            if (Math.abs(targetRotationY - currentRotationY) > 0.1) {
                currentRotationY += (targetRotationY - currentRotationY) * ROTATION_SPEED;
                needsUpdate = true;
            } else {
                currentRotationY = targetRotationY;  // Snap to exact target when close
            }
            
            // Repaint if anything changed
            if (needsUpdate || floatingUp || !floatingUp) {
                dominoPanel.repaint();
            } else if (isAnimating) {
                isAnimating = false;  // Animation complete
            }
            
        });
        
        animationTimer.start();  // Start the timer
        
    }
    
     
    
     /**
     * Inner class for drawing the 3D domino
     * This handles all the 3D rendering of the domino
     */
    class DominoPanel extends JPanel {
        // 3D coordinates of domino vertices for each face
        private double[][][] faceVertices;
        
        // Constants for face indices
        private static final int FRONT = 0;
        private static final int BACK = 1;
        private static final int RIGHT = 2;
        private static final int LEFT = 3;
        private static final int TOP = 4;
        private static final int BOTTOM = 5;
        
        // Array of dot positions for each domino value (1-6)
        private final Point2D.Double[][] dotPositions = {
            {}, // 0 (not used)
            {new Point2D.Double(0.5, 0.5)}, // 1: center dot
            {new Point2D.Double(0.25, 0.25), new Point2D.Double(0.75, 0.75)}, // 2: diagonal dots
            {new Point2D.Double(0.25, 0.25), new Point2D.Double(0.5, 0.5), new Point2D.Double(0.75, 0.75)}, // 3: diagonal + center
            {new Point2D.Double(0.25, 0.25), new Point2D.Double(0.25, 0.75), 
             new Point2D.Double(0.75, 0.25), new Point2D.Double(0.75, 0.75)}, // 4: corners
            {new Point2D.Double(0.25, 0.25), new Point2D.Double(0.25, 0.75), new Point2D.Double(0.5, 0.5), 
             new Point2D.Double(0.75, 0.25), new Point2D.Double(0.75, 0.75)}, // 5: corners + center
            {new Point2D.Double(0.25, 0.25), new Point2D.Double(0.25, 0.5), new Point2D.Double(0.25, 0.75), 
             new Point2D.Double(0.75, 0.25), new Point2D.Double(0.75, 0.5), new Point2D.Double(0.75, 0.75)} // 6: 2 columns of 3
        };
        
        /**
         * Constructor: Initialize the panel and vertex coordinates
         */
        public DominoPanel() {
                initFaceVertices();  // Set up 3D coordinates
                setCursor(new Cursor(Cursor.HAND_CURSOR));  // Show hand cursor by default
          }
        
        /**
         * Initialize the 3D coordinates for each face of the domino
         * This sets up the positions of all vertices in 3D space
         */
        private void initFaceVertices() {
            
            // Create 3D array: [face][vertex][coordinate]
            faceVertices = new double[6][4][3];
            
            // Calculate half dimensions for easier positioning
            double w = DOMINO_WIDTH / 2.0;
            double h = DOMINO_HEIGHT / 2.0;
            double d = DOMINO_DEPTH / 2.0;
            
            // FRONT face vertices (clockwise from top-left)
            faceVertices[FRONT][0] = new double[]{-w, -h, d};  // Top-left
            faceVertices[FRONT][1] = new double[]{w, -h, d};   // Top-right
            faceVertices[FRONT][2] = new double[]{w, h, d};    // Bottom-right
            faceVertices[FRONT][3] = new double[]{-w, h, d};   // Bottom-left
            
            // BACK face vertices (clockwise from top-left)
            faceVertices[BACK][0] = new double[]{w, -h, -d};    // Top-left
            faceVertices[BACK][1] = new double[]{-w, -h, -d};   // Top-right
            faceVertices[BACK][2] = new double[]{-w, h, -d};    // Bottom-right
            faceVertices[BACK][3] = new double[]{w, h, -d};     // Bottom-left
            
            // RIGHT face vertices
            faceVertices[RIGHT][0] = new double[]{w, -h, d};    // Top-front
            faceVertices[RIGHT][1] = new double[]{w, -h, -d};   // Top-back
            faceVertices[RIGHT][2] = new double[]{w, h, -d};    // Bottom-back
            faceVertices[RIGHT][3] = new double[]{w, h, d};     // Bottom-front
            
            // LEFT face vertices
            faceVertices[LEFT][0] = new double[]{-w, -h, -d};   // Top-back
            faceVertices[LEFT][1] = new double[]{-w, -h, d};    // Top-front
            faceVertices[LEFT][2] = new double[]{-w, h, d};     // Bottom-front
            faceVertices[LEFT][3] = new double[]{-w, h, -d};    // Bottom-back
            
            // TOP face vertices
            faceVertices[TOP][0] = new double[]{-w, -h, -d};    // Left-back
            faceVertices[TOP][1] = new double[]{w, -h, -d};     // Right-back
            faceVertices[TOP][2] = new double[]{w, -h, d};      // Right-front
            faceVertices[TOP][3] = new double[]{-w, -h, d};     // Left-front
            
            // BOTTOM face vertices
            faceVertices[BOTTOM][0] = new double[]{-w, h, d};    // Left-front
            faceVertices[BOTTOM][1] = new double[]{w, h, d};     // Right-front
            faceVertices[BOTTOM][2] = new double[]{w, h, -d};    // Right-back
            faceVertices[BOTTOM][3] = new double[]{-w, h, -d};   // Left-back
        }
        
        
        /**
         * Paint the domino on the panel
         * This is called automatically whenever the panel needs to be redrawn
         */
        @Override
        protected void paintComponent(Graphics g) {
            
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                
                // Enable antialiasing for smoother edges
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                        RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, 
                        RenderingHints.VALUE_RENDER_QUALITY);
                
                // Draw background gradient
                GradientPaint bgGradient = new GradientPaint(
                    0, 0, new Color(25, 25, 30),  // Start color (top)
                    getWidth(), getHeight(), new Color(10, 10, 15)  // End color (bottom)
                );
                g2d.setPaint(bgGradient);
                g2d.fillRect(0, 0, getWidth(), getHeight());  // Fill entire panel

                // Find center of panel
                int centerX = getWidth() / 2;
                int centerY = getHeight() / 2;

                // Apply floating animation effect
                double scaleFloat = 1.0 + Math.abs(floatOffset) / 100.0;  // Subtle scale change

                // Move to center and apply floating offset
                g2d.translate(centerX, centerY + floatOffset);
                g2d.scale(scaleFloat, scaleFloat);  // Scale slightly based on float position

                // Calculate which faces are visible from current angle
                int[] faceVisibility = calculateFaceVisibility();

                // Draw faces in back-to-front order
                for (int i = 0; i < 6; i++) {
                    drawFace(g2d, faceVisibility[i]);
                }

        }
        
        
        
        /**
         * Calculate which order to draw faces based on current rotation
         * This is important for correct depth perception (back faces drawn first)
         */
        
        private int[] calculateFaceVisibility() {
            // Calculate center point of each face after rotation
            double[][] faceCenters = new double[6][3];
            for (int face = 0; face < 6; face++) {
                double[] center = {0, 0, 0};
                // Average the 4 corners to find center
                for (int i = 0; i < 4; i++) {
                    center[0] += faceVertices[face][i][0];  // x
                    center[1] += faceVertices[face][i][1];  // y
                    center[2] += faceVertices[face][i][2];  // z
                }
                // Divide by 4 to get average
                center[0] /= 4;
                center[1] /= 4;
                center[2] /= 4;
                
                // Apply current rotation to center point
                faceCenters[face] = rotatePoint(center);
                
            }
            
            // Sort faces by z-coordinate (back to front)
            int[] faceOrder = {0, 1, 2, 3, 4, 5};
            for (int i = 0; i < 5; i++) {
                for (int j = i + 1; j < 6; j++) {
                    if (faceCenters[faceOrder[i]][2] > faceCenters[faceOrder[j]][2]) {
                        // Swap if wrong order
                        int temp = faceOrder[i];
                        faceOrder[i] = faceOrder[j];
                        faceOrder[j] = temp;
                    }
                    
                }
                
            }
            
            return faceOrder;
        }
        
        
        /**
         * Draw a single face of the domino
         * This handles the drawing of one complete face with all its details
         */
        private void drawFace(Graphics2D g2d, int face) {
            // Project 3D points to 2D for drawing
            Point2D.Double[] projectedPoints = new Point2D.Double[4];
            for (int i = 0; i < 4; i++) {
                // Apply rotation to each point
                double[] rotated = rotatePoint(faceVertices[face][i]);
                
                // Apply perspective scale (farther = smaller)
                double scale = 1.2 + rotated[2] / 1000.0;
                
                // Convert to 2D coordinates
                projectedPoints[i] = new Point2D.Double(
                        rotated[0] * scale,
                        rotated[1] * scale
                );
                
            }
            
            // Create path for the face
            Path2D.Double facePath = new Path2D.Double();
            facePath.moveTo(projectedPoints[0].x, projectedPoints[0].y);
            for (int i = 1; i < 4; i++) {
                facePath.lineTo(projectedPoints[i].x, projectedPoints[i].y);
            }
            facePath.closePath();
            
            // Draw differently based on which face it is
            if (face == FRONT || face == BACK) {
                // Draw the main face with gradient
                GradientPaint faceGradient = new GradientPaint(
                    (float)projectedPoints[0].x, (float)projectedPoints[0].y, 
                    brightenColor(dominoColor, 0.1f),  // Lighter at top
                    (float)projectedPoints[2].x, (float)projectedPoints[2].y, 
                    darkenColor(dominoColor, 0.1f)     // Darker at bottom
                );
                g2d.setPaint(faceGradient);
                g2d.fill(facePath);
                
                // Draw the dividing line between top and bottom halves
                Point2D.Double leftMiddle = new Point2D.Double(
                    (projectedPoints[0].x + projectedPoints[3].x) / 2,  // Middle of left edge
                    (projectedPoints[0].y + projectedPoints[3].y) / 2
                );
                Point2D.Double rightMiddle = new Point2D.Double(
                    (projectedPoints[1].x + projectedPoints[2].x) / 2,  // Middle of right edge
                    (projectedPoints[1].y + projectedPoints[2].y) / 2
                );
                
                // Draw divider line
                g2d.setColor(dividerColor);
                g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
                g2d.draw(new Line2D.Double(leftMiddle, rightMiddle));
                
                if (face == FRONT) {
                    // Draw dots on front face
                    drawDots(g2d, projectedPoints[0], projectedPoints[1], leftMiddle, topValue);
                    drawDots(g2d, leftMiddle, rightMiddle, projectedPoints[3], bottomValue);
                } else {
                    // Draw dots on back face (reversed order)
                    drawDots(g2d, projectedPoints[0], projectedPoints[1], leftMiddle, bottomValue);
                    drawDots(g2d, leftMiddle, rightMiddle, projectedPoints[3], topValue);
                }
                
            } else {
                // Draw side faces with gradient
                GradientPaint faceGradient = new GradientPaint(
                    (float)projectedPoints[0].x, (float)projectedPoints[0].y, 
                    brightenColor(dominoColor, 0.1f),
                    (float)projectedPoints[2].x, (float)projectedPoints[2].y, 
                    darkenColor(dominoColor, 0.1f)
                );
                g2d.setPaint(faceGradient);
                g2d.fill(facePath);
                
            }
            
            // Draw face border
            g2d.setColor(new Color(40, 40, 40, 100));
            g2d.setStroke(new BasicStroke(1.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
            g2d.draw(facePath);
            
        }
        
        
        
        /**
         * Draw the dots on a face section
         * This places the dots in the correct positions on each half of the domino
         */
        private void drawDots(Graphics2D g2d, Point2D.Double topLeft, Point2D.Double topRight, 
                             Point2D.Double bottomLeft, int value) {
            
            if (value < 1 || value > 6) return;  // Invalid value
            
            // Calculate dot size based on face dimensions
            double width = topRight.distance(topLeft);
            double height = bottomLeft.distance(topLeft);
            double dotSize = Math.min(width, height) * 0.15;  // Size relative to face
            
            // Draw each dot for this value
            for (Point2D.Double pos : dotPositions[value]) {
                // Calculate dot position using relative coordinates
                double x = topLeft.x + (topRight.x - topLeft.x) * pos.x + (bottomLeft.x - topLeft.x) * pos.y;
                double y = topLeft.y + (topRight.y - topLeft.y) * pos.x + (bottomLeft.y - topLeft.y) * pos.y;
                
                // Create gradient for dot
                RadialGradientPaint dotGradient = new RadialGradientPaint(
                    new Point2D.Double(x - dotSize/4, y - dotSize/4),  // Offset for 3D effect
                    (float)dotSize,
                    new float[]{0.0f, 1.0f},
                    new Color[]{brightenColor(dotColor, 0.3f), dotColor}  // Lighter center, darker edges
                );
                
                // Draw dot
                g2d.setPaint(dotGradient);
                g2d.fill(new Ellipse2D.Double(x - dotSize/2, y - dotSize/2, dotSize, dotSize));
                
                // Add subtle border to dot
                g2d.setColor(new Color(0, 0, 0, 50));
                g2d.draw(new Ellipse2D.Double(x - dotSize/2, y - dotSize/2, dotSize, dotSize));
            }
        }
        
        
        /**
         * Apply rotation transformations to a 3D point
         * This converts a 3D point based on the current rotation angles
         */
        private double[] rotatePoint(double[] point) {
            double[] result = new double[3];
            double[] temp = new double[3];
            
            // Convert degrees to radians for math functions
            double radX = Math.toRadians(currentRotationX);
            double radY = Math.toRadians(currentRotationY);
            
            // Copy point
            System.arraycopy(point, 0, temp, 0, 3);
            
            // X rotation
            result[0] = temp[0];
            result[1] = temp[1] * Math.cos(radX) - temp[2] * Math.sin(radX);
            result[2] = temp[1] * Math.sin(radX) + temp[2] * Math.cos(radX);
            
            System.arraycopy(result, 0, temp, 0, 3);
            
            // Y rotation
            result[0] = temp[0] * Math.cos(radY) + temp[2] * Math.sin(radY);
            result[1] = temp[1];
            result[2] = -temp[0] * Math.sin(radY) + temp[2] * Math.cos(radY);
            
            System.arraycopy(result, 0, temp, 0, 3);
            
            return result;
        }
        
          
        private Color brightenColor(Color color, float factor) {
            float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
            float brightness = Math.min(1.0f, hsb[2] + factor);
            return Color.getHSBColor(hsb[0], hsb[1], brightness);
        }
        
        private Color darkenColor(Color color, float factor) {
            float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
            float brightness = Math.max(0.0f, hsb[2] - factor);
            return Color.getHSBColor(hsb[0], hsb[1], brightness);
        }
        
    
    }
     
     
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
                e.printStackTrace();
            }
            new Domino3D().setVisible(true);
        });
    }
    
}


  


The Final Result:

3D Domino in Java Netbeans

How to Create a 3D Domino Piece in Java

How to Create a 3D Domino Piece in Java Swing

3D Domino Piece in Java Swing



if you want the source code click on the download button below