package eu.svjatoslav.sixth.e3d.gui;
-import eu.svjatoslav.sixth.e3d.gui.humaninput.KeyboardFocusTracker;
-import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
-import eu.svjatoslav.sixth.e3d.gui.humaninput.UserInputTracker;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.HIDInputTracker;
+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.ComponentEvent;
import java.awt.event.ComponentListener;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
import java.util.Timer;
+import java.util.concurrent.ConcurrentHashMap;
+/**
+ * Java Swing GUI panel that contains canvas for 3D rendering.
+ */
public class ViewPanel extends JPanel implements ComponentListener {
- private final UserInputTracker userInputTracker = new UserInputTracker(this);
-
- private final KeyboardFocusTracker keyboardFocusTracker = new KeyboardFocusTracker(
- this);
-
+ private static final long serialVersionUID = 1683277888885045387L;
+ public Color backgroundColor = Color.BLACK;
+ private final HIDInputTracker HIDInputTracker = new HIDInputTracker(this);
+ private final KeyboardFocusStack keyboardFocusStack;
private final Avatar avatar = new Avatar();
-
private final ShapeCollection rootShapeCollection = new ShapeCollection();
-
-
- public Avatar getAvatar() {
- return avatar;
- }
-
- public KeyboardFocusTracker getKeyboardFocusTracker() {
- return keyboardFocusTracker;
- }
-
- public ShapeCollection getRootShapeCollection() {
- return rootShapeCollection;
- }
-
- public UserInputTracker getUserInputTracker() {
- return userInputTracker;
- }
-
- private static final long serialVersionUID = 1683277888885045387L;
- private final List<ViewRenderListener> viewRenderListeners = new ArrayList<>();
+ private final Set<ViewRenderListener> viewRenderListeners = ConcurrentHashMap.newKeySet();
/**
* Last time this view was updated.
*/
private Timer canvasUpdateTimer;
private ViewUpdateTimerTask canvasUpdateTimerTask;
private RenderingContext renderingContext = null;
-
- /**
- * UI component that mouse is currently hovering over.
- */
- private MouseInteractionController currentMouseOverComponent;
-
/**
* Currently target FPS for this view. It can be changed at runtime. Also when nothing
* changes in the view, then frames are not really repainted.
*/
private int targetFPS = 30;
-
/**
* Set to true if it is known than next frame reeds to be painted. Flag is cleared
* immediately after frame got updated.
public ViewPanel() {
viewRenderListeners.add(avatar);
- viewRenderListeners.add(userInputTracker);
+ viewRenderListeners.add(HIDInputTracker);
+
+ keyboardFocusStack = new KeyboardFocusStack(this);
initializePanelLayout();
addComponentListener(this);
}
+ public Avatar getAvatar() {
+ return avatar;
+ }
+
+ public KeyboardFocusStack getKeyboardFocusStack() {
+ return keyboardFocusStack;
+ }
+
+ public ShapeCollection getRootShapeCollection() {
+ return rootShapeCollection;
+ }
+
+ public HIDInputTracker getHIDInputTracker() {
+ return HIDInputTracker;
+ }
+
public void addViewUpdateListener(final ViewRenderListener listener) {
viewRenderListeners.add(listener);
}
return new java.awt.Dimension(640, 480);
}
- public RenderingContext getRenderBuffer() {
- return renderingContext;
- }
-
public RenderingContext getRenderingContext() {
return renderingContext;
}
- private void handleDetectedComponentMouseEvents() {
- if (renderingContext.clickedItem != null) {
- if (renderingContext.mouseClick.button == 0) {
- // mouse over
- if (currentMouseOverComponent == null) {
- currentMouseOverComponent = renderingContext.clickedItem;
- currentMouseOverComponent.mouseEntered();
- viewRepaintNeeded = true;
- } else if (currentMouseOverComponent != renderingContext.clickedItem) {
- currentMouseOverComponent.mouseExited();
- currentMouseOverComponent = renderingContext.clickedItem;
- currentMouseOverComponent.mouseEntered();
- viewRepaintNeeded = true;
- }
- } else {
- // mouse click
- renderingContext.clickedItem.mouseClicked();
- viewRepaintNeeded = true;
- }
- } else if (currentMouseOverComponent != null) {
- currentMouseOverComponent.mouseExited();
- viewRepaintNeeded = true;
- currentMouseOverComponent = null;
- }
- }
-
private void initializePanelLayout() {
setFocusCycleRoot(true);
setOpaque(true);
requestFocusInWindow();
}
- public void renderFrame() {
- // build new render buffer if needed, this happens when window was just
- // created or resized
- if ((renderingContext == null)
- || (renderingContext.width != getWidth())
- || (renderingContext.height != getHeight()))
- renderingContext = new RenderingContext(getWidth(), getHeight());
-
- // clear drawing area
- {
- renderingContext.graphics.setColor(Color.BLACK);
- renderingContext.graphics.fillRect(0, 0, getWidth(), getHeight());
- }
-
+ private void renderFrame() {
// paint root geometry collection to the offscreen render buffer
+ clearCanvas();
rootShapeCollection.paint(this, renderingContext);
- // draw rendered offscreen image to visible screen
+ // draw rendered offscreen buffer to visible screen
final Graphics graphics = getGraphics();
if (graphics != null)
graphics.drawImage(renderingContext.bufferedImage, 0, 0, null);
}
+ private void clearCanvas() {
+ renderingContext.graphics.setColor(backgroundColor);
+ renderingContext.graphics.fillRect(0, 0, getWidth(), getHeight());
+ }
+
/**
* Calling this methods tells 3D engine that current 3D view needs to be
* repainted on first opportunity.
* It tells view to update itself. View can decide if actual re-rendering of
* graphics is needed.
*/
- public void updateView() {
- if (renderingContext != null){
- renderingContext.mouseClick = null;
- renderingContext.clickedItem = null;
+ void updateView() {
+ maintainRenderingContext();
+
+ final int millisecondsPassedSinceLastUpdate = getMillisecondsPassedSinceLastUpdate();
+
+ boolean renderFrame = notifyViewRenderListeners(millisecondsPassedSinceLastUpdate);
+
+ if (viewRepaintNeeded) {
+ viewRepaintNeeded = false;
+ renderFrame = true;
}
- // compute time passed since last view update
- final long currentTime = System.currentTimeMillis();
+ // abort rendering if window size is invalid
+ if ((getWidth() > 0) && (getHeight() > 0) && renderFrame) {
+ renderFrame();
+ viewRepaintNeeded = renderingContext.handleDetectedComponentMouseEvents();
+ }
- if (lastUpdateMillis == 0) {
- lastUpdateMillis = currentTime;
+ }
+
+ private void maintainRenderingContext() {
+ int panelWidth = getWidth();
+ int panelHeight = getHeight();
+
+ if (panelWidth <= 0 || panelHeight <=0){
+ renderingContext = null;
return;
}
- final int millisecondsPassedSinceLastUpdate = (int) (currentTime - lastUpdateMillis);
- lastUpdateMillis = currentTime;
+ if ((renderingContext == null)
+ || (renderingContext.width != panelWidth)
+ || (renderingContext.height != panelHeight)) {
+ renderingContext = new RenderingContext(panelWidth, panelHeight);
+ }
- // notify update listeners
- boolean reRenderFrame = false;
+ renderingContext.prepareForNewFrameRendering();
+ }
+ private boolean notifyViewRenderListeners(int millisecondsPassedSinceLastUpdate) {
+ boolean reRenderFrame = false;
for (final ViewRenderListener listener : viewRenderListeners)
if (listener.beforeRender(this, millisecondsPassedSinceLastUpdate))
reRenderFrame = true;
+ return reRenderFrame;
+ }
- // abort rendering if window size is invalid
- if ((getWidth() <= 0) || (getHeight() <= 0))
- return;
+ private int getMillisecondsPassedSinceLastUpdate() {
+ final long currentTime = System.currentTimeMillis();
- if (viewRepaintNeeded) {
- viewRepaintNeeded = false;
- reRenderFrame = true;
- }
+ if (lastUpdateMillis == 0)
+ lastUpdateMillis = currentTime;
- if (reRenderFrame) {
- renderFrame();
- handleDetectedComponentMouseEvents();
- }
+ final int millisecondsPassedSinceLastUpdate = (int) (currentTime - lastUpdateMillis);
+ lastUpdateMillis = currentTime;
+ return millisecondsPassedSinceLastUpdate;
}
+
+ public void addViewRenderListener(ViewRenderListener viewRenderListener) {
+ viewRenderListeners.add(viewRenderListener);
+ }
+
+ public void removeViewRenderListener(ViewRenderListener viewRenderListener) {
+ viewRenderListeners.remove(viewRenderListener);
+ }
+
}