import eu.svjatoslav.sixth.e3d.gui.humaninput.KeyboardFocusStack;
import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
-import javax.swing.*;
import java.awt.*;
+import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
-import java.awt.event.ComponentListener;
+import java.awt.image.BufferStrategy;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
- * Java Swing panel that provides a 3D rendering canvas with built-in camera navigation.
+ * AWT Canvas that provides a 3D rendering surface with built-in camera navigation.
*
* <p>{@code ViewPanel} is the primary entry point for embedding the Sixth 3D engine into
- * a Java Swing application. It manages the render loop, maintains a scene graph
+ * a Java application. It manages the render loop, maintains a scene graph
* ({@link ShapeCollection}), and handles user input for camera navigation.</p>
*
+ * <p>Uses {@link BufferStrategy} for efficient page-flipping and tear-free rendering.</p>
+ *
* <p><b>Quick start - creating a 3D view in a window:</b></p>
* <pre>{@code
* // Option 1: Use ViewFrame (creates a maximized JFrame for you)
* ViewFrame frame = new ViewFrame();
* ViewPanel viewPanel = frame.getViewPanel();
*
- * // Option 2: Embed ViewPanel in your own Swing layout
+ * // Option 2: Embed ViewPanel in your own window
* JFrame frame = new JFrame("My 3D App");
* ViewPanel viewPanel = new ViewPanel();
* frame.add(viewPanel);
* @see Camera the camera/viewer
* @see FrameListener for per-frame callbacks
*/
-public class ViewPanel extends JPanel implements ComponentListener {
+public class ViewPanel extends Canvas {
private static final long serialVersionUID = 1683277888885045387L;
+ private static final int NUM_BUFFERS = 2;
+
private final InputManager inputManager = new InputManager(this);
private final KeyboardFocusStack keyboardFocusStack;
private final Camera camera = new Camera();
private long nextFrameTime;
+ private BufferStrategy bufferStrategy;
+
+ private boolean bufferStrategyInitialized = false;
+
public ViewPanel() {
frameListeners.add(camera);
frameListeners.add(inputManager);
keyboardFocusStack = new KeyboardFocusStack(this);
- initializePanelLayout();
+ initializeCanvas();
startRenderThread();
- addComponentListener(this);
+ addComponentListener(new ComponentAdapter() {
+ @Override
+ public void componentResized(final ComponentEvent e) {
+ viewRepaintNeeded = true;
+ }
+
+ @Override
+ public void componentShown(final ComponentEvent e) {
+ viewRepaintNeeded = true;
+ }
+ });
}
- /**
- * Returns the camera that represents the viewer's position and
- * orientation in the 3D world. Use this to programmatically move the camera.
- *
- * @return the camera for this view
- */
public Camera getCamera() {
return camera;
}
}
@Override
- public void componentHidden(final ComponentEvent e) {
-
- }
-
- @Override
- public void componentMoved(final ComponentEvent e) {
-
- }
-
- @Override
- public void componentResized(final ComponentEvent e) {
- viewRepaintNeeded = true;
- }
-
- @Override
- public void componentShown(final ComponentEvent e) {
- viewRepaintNeeded = true;
- }
-
- @Override
- public Dimension getMaximumSize() {
- return getPreferredSize();
+ public Dimension getPreferredSize() {
+ return new Dimension(640, 480);
}
@Override
}
@Override
- public java.awt.Dimension getPreferredSize() {
- return new java.awt.Dimension(640, 480);
+ public Dimension getMaximumSize() {
+ return getPreferredSize();
}
public RenderingContext getRenderingContext() {
return renderingContext;
}
- private void initializePanelLayout() {
- setFocusCycleRoot(true);
- setOpaque(true);
+ private void initializeCanvas() {
setFocusable(true);
- setDoubleBuffered(false);
setVisible(true);
- requestFocusInWindow();
+ requestFocus();
+ }
+
+ private void ensureBufferStrategy() {
+ if (bufferStrategyInitialized && bufferStrategy != null)
+ return;
+
+ if (!isDisplayable())
+ return;
+
+ try {
+ createBufferStrategy(NUM_BUFFERS);
+ bufferStrategy = getBufferStrategy();
+ bufferStrategyInitialized = true;
+ } catch (final Exception e) {
+ bufferStrategy = null;
+ bufferStrategyInitialized = false;
+ }
}
private void renderFrame() {
- // paint root geometry collection to the offscreen render buffer
+ ensureBufferStrategy();
+
+ if (bufferStrategy == null)
+ return;
+
clearCanvas();
rootShapeCollection.paint(this, renderingContext);
- // draw rendered offscreen buffer to visible screen
- final Graphics graphics = getGraphics();
- if (graphics != null)
- graphics.drawImage(renderingContext.bufferedImage, 0, 0, null);
+ Graphics2D g = null;
+ try {
+ g = (Graphics2D) bufferStrategy.getDrawGraphics();
+ g.drawImage(renderingContext.bufferedImage, 0, 0, null);
+ } finally {
+ if (g != null)
+ g.dispose();
+ }
+
+ if (!bufferStrategy.contentsLost())
+ bufferStrategy.show();
}
private void clearCanvas() {
renderFrame();
viewRepaintNeeded = renderingContext.handlePossibleComponentMouseEvent();
}
-
}
private void maintainRenderingContext() {
frameListeners.remove(frameListener);
}
-}
+}
\ No newline at end of file
import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
import eu.svjatoslav.sixth.e3d.math.Rotation;
-import javax.swing.*;
+import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Manages mouse and keyboard input for the 3D view.
*
- * <p>Handles Swing mouse/keyboard events, tracks pressed keys and mouse state,
+ * <p>Handles mouse/keyboard events, tracks pressed keys and mouse state,
* and forwards events to the appropriate handlers. Also provides default camera
* control via mouse dragging (look around) and mouse wheel (vertical movement).</p>
*
return viewUpdateNeeded;
}
- private void bind(final JPanel panel) {
- panel.addMouseMotionListener(this);
- panel.addKeyListener(this);
- panel.addMouseListener(this);
- panel.addMouseWheelListener(this);
+ private void bind(final Component component) {
+ component.addMouseMotionListener(this);
+ component.addKeyListener(this);
+ component.addMouseListener(this);
+ component.addMouseWheelListener(this);
}
private boolean handleKeyboardEvents() {