2 * Sixth 3D engine. Copyright ©2012-2018, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 3 of the GNU Lesser General Public License
6 * or later as published by the Free Software Foundation.
10 package eu.svjatoslav.sixth.e3d.gui;
12 import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
16 import java.awt.event.ComponentEvent;
17 import java.awt.event.ComponentListener;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Timer;
22 public class ViewPanel extends JPanel implements ComponentListener {
24 private static final long serialVersionUID = 1683277888885045387L;
25 private final List<ViewRenderListener> viewRenderListeners = new ArrayList<>();
26 private final ViewContext context = new ViewContext(this);
28 * Last time this view was updated.
30 private long lastUpdateMillis = 0;
31 private Timer canvasUpdateTimer;
32 private ViewUpdateTimerTask canvasUpdateTimerTask;
33 private RenderingContext renderingContext = null;
34 private MouseInteractionController currentMouseOverComponent;
36 * Currently target FPS for this view. It might change at runtime.
38 private int targetFramerate = 30;
39 private boolean repaintDuringNextViewUpdate = true;
42 viewRenderListeners.add(context.getAvatar());
44 // initialize input tracker
45 context.getUserInputTracker().bind(this);
46 viewRenderListeners.add(context.getUserInputTracker());
48 initializePanelLayout();
50 setFrameRate(targetFramerate);
52 addComponentListener(this);
55 public void addViewUpdateListener(final ViewRenderListener listener) {
56 viewRenderListeners.add(listener);
60 public void componentHidden(final ComponentEvent e) {
65 public void componentMoved(final ComponentEvent e) {
70 public void componentResized(final ComponentEvent e) {
71 repaintDuringNextViewUpdate = true;
75 public void componentShown(final ComponentEvent e) {
76 repaintDuringNextViewUpdate = true;
79 public ViewContext getContext() {
84 public Dimension getMaximumSize() {
85 return getPreferredSize();
89 public Dimension getMinimumSize() {
90 return getPreferredSize();
94 public java.awt.Dimension getPreferredSize() {
95 return new java.awt.Dimension(640, 480);
98 public RenderingContext getRenderBuffer() {
99 return renderingContext;
102 public RenderingContext getRenderingContext() {
103 return renderingContext;
106 private void handleDetectedComponentMouseEvents() {
107 if (renderingContext.clickedItem != null) {
108 if (renderingContext.mouseClick.button == 0) {
110 if (currentMouseOverComponent == null) {
111 currentMouseOverComponent = renderingContext.clickedItem;
112 currentMouseOverComponent.mouseEntered();
113 repaintDuringNextViewUpdate = true;
114 } else if (currentMouseOverComponent != renderingContext.clickedItem) {
115 currentMouseOverComponent.mouseExited();
116 currentMouseOverComponent = renderingContext.clickedItem;
117 currentMouseOverComponent.mouseEntered();
118 repaintDuringNextViewUpdate = true;
122 renderingContext.clickedItem.mouseClicked();
123 repaintDuringNextViewUpdate = true;
125 } else if (currentMouseOverComponent != null) {
126 currentMouseOverComponent.mouseExited();
127 repaintDuringNextViewUpdate = true;
128 currentMouseOverComponent = null;
132 private void initializePanelLayout() {
133 setFocusCycleRoot(true);
136 setDoubleBuffered(false);
138 requestFocusInWindow();
141 public void renderFrame() {
142 // build new render buffer if needed, this happens when window was just
143 // created or resized
144 if ((renderingContext == null)
145 || (renderingContext.width != getWidth())
146 || (renderingContext.height != getHeight()))
147 renderingContext = new RenderingContext(getWidth(), getHeight());
149 // clear drawing area
151 renderingContext.graphics.setColor(Color.BLACK);
152 renderingContext.graphics.fillRect(0, 0, getWidth(), getHeight());
155 // paint root geometry collection to the offscreen render buffer
156 context.getRootShapeCollection().paint(context, renderingContext);
158 // draw rendered offscreen image to visible screen
159 final Graphics graphics = getGraphics();
160 if (graphics != null)
161 graphics.drawImage(renderingContext.bufferedImage, 0, 0, null);
165 * Calling this methods tells 3D engine that current 3D view needs to be
166 * repainted on first opportunity.
168 public void repaintDuringNextViewUpdate() {
169 repaintDuringNextViewUpdate = true;
172 public void setFrameRate(final int frameRate) {
173 if (canvasUpdateTimerTask != null) {
174 canvasUpdateTimerTask.cancel();
175 canvasUpdateTimerTask = null;
177 canvasUpdateTimer.cancel();
178 canvasUpdateTimer = null;
181 targetFramerate = frameRate;
184 canvasUpdateTimer = new Timer();
185 canvasUpdateTimerTask = new ViewUpdateTimerTask(this);
187 canvasUpdateTimer.schedule(canvasUpdateTimerTask, 0,
193 if (canvasUpdateTimerTask != null) {
194 canvasUpdateTimerTask.cancel();
195 canvasUpdateTimerTask = null;
198 if (canvasUpdateTimer != null) {
199 canvasUpdateTimer.cancel();
200 canvasUpdateTimer = null;
205 * This method is executed by periodic timer task, in frequency according to
206 * defined frame rate.
208 * It tells view to update itself. View can decide if actual re-rendering of
209 * graphics is needed.
211 public void updateView() {
212 if (renderingContext != null){
213 renderingContext.mouseClick = null;
214 renderingContext.clickedItem = null;
217 // compute time passed since last view update
218 final long currentTime = System.currentTimeMillis();
220 if (lastUpdateMillis == 0) {
221 lastUpdateMillis = currentTime;
225 final int millisecondsPassedSinceLastUpdate = (int) (currentTime - lastUpdateMillis);
226 lastUpdateMillis = currentTime;
228 // notify update listeners
229 boolean reRenderFrame = false;
231 for (final ViewRenderListener listener : viewRenderListeners)
232 if (listener.beforeRender(context,
233 millisecondsPassedSinceLastUpdate))
234 reRenderFrame = true;
236 // abort rendering if window size is invalid
237 if ((getWidth() <= 0) || (getHeight() <= 0))
240 if (repaintDuringNextViewUpdate) {
241 repaintDuringNextViewUpdate = false;
242 reRenderFrame = true;
247 handleDetectedComponentMouseEvents();