-/*
- * Sixth 3D engine. Copyright ©2012-2018, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 3 of the GNU Lesser General Public License
- * or later as published by the Free Software Foundation.
- *
- */
-
-package eu.svjatoslav.sixth.e3d.gui;
-
-import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
-
-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.Timer;
-
-public class View extends JPanel implements ComponentListener {
-
- private static final long serialVersionUID = 1683277888885045387L;
- private final List<ViewListener> viewListeners = new ArrayList<>();
- private final List<ViewRenderListener> viewRenderListeners = new ArrayList<>();
- private final ViewContext context = new ViewContext(this);
- /**
- * Last time this view was updated
- */
- long lastUpdateMillis = 0;
- private Timer canvasUpdateTimer;
- private ViewUpdateTimerTask canvasUpdateTimerTask;
- private RenderingContext renderingContext = null;
- private MouseInteractionController currentMouseOverComponent;
- /**
- * Currently target FPS for this view. It might change at runtime.
- */
- private int targetFramerate = 30;
- private boolean repaintDuringNextViewUpdate = true;
-
- public View() {
- viewRenderListeners.add(context.getAvatar());
-
- // initialize input tracker
- context.getUserInputTracker().bind(this);
- viewRenderListeners.add(context.getUserInputTracker());
-
- initializePanelLayout();
-
- setFrameRate(targetFramerate);
-
- addComponentListener(this);
- }
-
- public void addViewListener(final ViewListener listener) {
- getViewListeners().add(listener);
- }
-
- public void addViewUpdateListener(final ViewRenderListener listener) {
- viewRenderListeners.add(listener);
- }
-
- @Override
- public void componentHidden(final ComponentEvent e) {
-
- }
-
- @Override
- public void componentMoved(final ComponentEvent e) {
-
- }
-
- @Override
- public void componentResized(final ComponentEvent e) {
- repaintDuringNextViewUpdate = true;
- }
-
- @Override
- public void componentShown(final ComponentEvent e) {
- repaintDuringNextViewUpdate = true;
- }
-
- public ViewContext getContext() {
- return context;
- }
-
- @Override
- public Dimension getMaximumSize() {
- return getPreferredSize();
- }
-
- @Override
- public Dimension getMinimumSize() {
- return getPreferredSize();
- }
-
- @Override
- public java.awt.Dimension getPreferredSize() {
- return new java.awt.Dimension(640, 480);
- }
-
- public RenderingContext getRenderBuffer() {
- return renderingContext;
- }
-
- public RenderingContext getRenderingContext() {
- return renderingContext;
- }
-
- public List<ViewListener> getViewListeners() {
- return viewListeners;
- }
-
- private void handleDetectedComponentMouseEvents() {
- if (renderingContext.clickedItem != null) {
- if (renderingContext.mouseClick.button == 0) {
- // mouse over
- if (currentMouseOverComponent == null) {
- currentMouseOverComponent = renderingContext.clickedItem;
- currentMouseOverComponent.mouseEntered();
- repaintDuringNextViewUpdate = true;
- } else if (currentMouseOverComponent != renderingContext.clickedItem) {
- currentMouseOverComponent.mouseExited();
- currentMouseOverComponent = renderingContext.clickedItem;
- currentMouseOverComponent.mouseEntered();
- repaintDuringNextViewUpdate = true;
- }
- } else {
- // mouse click
- renderingContext.clickedItem.mouseClicked();
- repaintDuringNextViewUpdate = true;
- }
- } else if (currentMouseOverComponent != null) {
- currentMouseOverComponent.mouseExited();
- repaintDuringNextViewUpdate = true;
- currentMouseOverComponent = null;
- }
- }
-
- private void initializePanelLayout() {
- setFocusCycleRoot(true);
- setOpaque(true);
- setFocusable(true);
- setDoubleBuffered(false);
- setVisible(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());
- }
-
- // paint root geometry collection to the offscreen render buffer
- context.getRootShapeCollection().paint(context, renderingContext);
-
- // draw rendered offscreen image to visible screen
- final Graphics graphics = getGraphics();
- if (graphics != null)
- graphics.drawImage(renderingContext.bufferedImage, 0, 0, null);
- }
-
- /**
- * Calling this methods tells 3D engine that current 3D view needs to be
- * repainted on first opportunity.
- */
- public void repaintDuringNextViewUpdate() {
- repaintDuringNextViewUpdate = true;
- }
-
- public void setFrameRate(final int frameRate) {
- if (canvasUpdateTimerTask != null) {
- canvasUpdateTimerTask.cancel();
- canvasUpdateTimerTask = null;
-
- canvasUpdateTimer.cancel();
- canvasUpdateTimer = null;
- }
-
- targetFramerate = frameRate;
-
- if (frameRate > 0) {
- canvasUpdateTimer = new Timer();
- canvasUpdateTimerTask = new ViewUpdateTimerTask(this);
-
- canvasUpdateTimer.schedule(canvasUpdateTimerTask, 0,
- 1000 / frameRate);
- }
- }
-
- public void stop() {
- if (canvasUpdateTimerTask != null) {
- canvasUpdateTimerTask.cancel();
- canvasUpdateTimerTask = null;
- }
-
- if (canvasUpdateTimer != null) {
- canvasUpdateTimer.cancel();
- canvasUpdateTimer = null;
- }
- }
-
- /**
- * This method is executed by periodic timer task, in frequency according to
- * defined frame rate.
- * <p>
- * 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;
- }
-
- // compute time passed since last view update
- final long currentTime = System.currentTimeMillis();
-
- if (lastUpdateMillis == 0) {
- lastUpdateMillis = currentTime;
- return;
- }
-
- final int millisecondsPassedSinceLastUpdate = (int) (currentTime - lastUpdateMillis);
- lastUpdateMillis = currentTime;
-
- // notify update listeners
- boolean reRenderFrame = false;
-
- for (final ViewRenderListener listener : viewRenderListeners)
- if (listener.beforeRender(context,
- millisecondsPassedSinceLastUpdate))
- reRenderFrame = true;
-
- // abort rendering if window size is invalid
- if ((getWidth() <= 0) || (getHeight() <= 0))
- return;
-
- if (repaintDuringNextViewUpdate) {
- repaintDuringNextViewUpdate = false;
- reRenderFrame = true;
- }
-
- if (reRenderFrame) {
- renderFrame();
- handleDetectedComponentMouseEvents();
- }
- }
-}