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








Share this

Related Posts

Latest
Previous
Next Post »