001/*
002 *   BGFGCanvas3D -- A Canvas3D that renders 2D background or overlay images with a 3D scene.
003 *   
004 *   Copyright (C) 2009-2023, by Joseph A. Huwaldt.
005 *   All rights reserved.
006 *   
007 *   This library is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *   
012 *   This library is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 *   Lesser General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public License
018 *   along with this program; if not, write to the Free Software
019 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
021 */
022package jahuwaldt.j3d;
023
024import java.awt.GraphicsConfiguration;
025import java.awt.image.BufferedImage;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.List;
029import static java.util.Objects.isNull;
030import static java.util.Objects.requireNonNull;
031import org.jogamp.java3d.*;
032
033/**
034 * BGFGCanvas3D is a <code>Canvas3D</code> that renders an list of arbitrary 2D
035 * {@link BGFGImage} objects either behind or over top of the 3D scene. This canvas also
036 * provides a callback that allows you to capture the contents of the canvas and write out
037 * the image information.
038 *
039 * <p> Modified by: Joseph A.Huwaldt </p>
040 *
041 * @author Joseph A. Huwaldt, Date: April 9, 2009
042 * @version June 4, 2023
043 */
044public class BGFGCanvas3D extends ImageCaptureCanvas3D {
045
046    private static final long serialVersionUID = 1L;
047
048    //  A list of background images.
049    private final List<BGFGImage> _backgrounds = Collections.synchronizedList(new ArrayList());
050
051    //  The list of overlays used by this canvas.
052    private final List<BGFGImage> _overlays = Collections.synchronizedList(new ArrayList());
053
054    /**
055     * Constructs and initializes a new BGFGCanvas3D object that Java 3D can render into.
056     *
057     * @param gconfig A valid GraphicsConfiguration object that will be used to create the
058     *                canvas. May not be null.
059     * @throws IllegalArgumentException if the specified GraphicsConfiguration does not
060     * support 3D rendering
061     */
062    public BGFGCanvas3D(GraphicsConfiguration gconfig) {
063        super(requireNonNull(gconfig, "gconfig == null"));
064    }
065
066    /**
067     * Constructs and initializes a new BGFGCanvas3D object that Java 3D can render into.
068     *
069     * @param gconfig   A valid GraphicsConfiguration object that will be used to create
070     *                  the canvas. May not be null.
071     * @param offscreen A flag that indicates whether this canvas is an off-screen 3D
072     *                  rendering canvas. Note that if offScreen is set to true, this
073     *                  Canvas3D object cannot be used for normal rendering; it should not
074     *                  be added to any Container object.
075     * @throws IllegalArgumentException if the specified GraphicsConfiguration does not
076     * support 3D rendering
077     */
078    public BGFGCanvas3D(GraphicsConfiguration gconfig, boolean offscreen) {
079        super(requireNonNull(gconfig, "gconfig == null"), offscreen);
080    }
081
082    /**
083     * Returns the number of background images associated with this canvas.
084     *
085     * @return The number of background images.
086     */
087    public int getNumberBackgrounds() {
088        return _backgrounds.size();
089    }
090
091    /**
092     * Return the specified background used by this canvas.
093     *
094     * @param index The index of the background image to retrieve.
095     * @return The background image at the specified index.
096     */
097    public BGFGImage getBackground(int index) {
098        return _backgrounds.get(index);
099    }
100
101    /**
102     * Set the specified background for use for this canvas.
103     *
104     * @param index      The index for the background to set.
105     * @param background The background image to set at the specified index. May not be
106     *                   null.
107     * @return The image that was at the specified index location.
108     */
109    public BGFGImage setBackground(int index, BGFGImage background) {
110        requireNonNull(background, "background == null");
111        BGFGImage old = _backgrounds.set(index, background);
112        return old;
113    }
114
115    /**
116     * Adds the specified background to this canvas.
117     *
118     * @param background The background to add to this canvas. May not be null.
119     */
120    public void addBackground(BGFGImage background) {
121        _backgrounds.add(requireNonNull(background, "background == null"));
122    }
123
124    /**
125     * Removes the specified background from this canvas.
126     *
127     * @param index The index for the background to remove.
128     * @return The image that used to be at the specified index.
129     */
130    public BGFGImage removeBackground(int index) {
131        return _backgrounds.remove(index);
132    }
133
134    /**
135     * Removes the specified background from this canvas.
136     *
137     * @param background The background to remove from this canvas. May not be null.
138     * @return true if this canvas contained the specified background image.
139     */
140    public boolean removeBackground(BGFGImage background) {
141        return _backgrounds.remove(requireNonNull(background, "background == null"));
142    }
143
144    /**
145     * Removes all the backgrounds from this canvas.
146     */
147    public void clearBackgrounds() {
148        _backgrounds.clear();
149    }
150
151    /**
152     * Returns the number of overlays/foregrounds associated with this canvas.
153     *
154     * @return The number of overlays.
155     */
156    public int getNumberOverlays() {
157        return _overlays.size();
158    }
159
160    /**
161     * Return the specified overlay used by this canvas.
162     *
163     * @param index The index of the overlay (foreground) to return.
164     * @return The overlay/foreground image at the specified index.
165     */
166    public BGFGImage getOverlay(int index) {
167        return _overlays.get(index);
168    }
169
170    /**
171     * Set the specified overlay for use for this canvas.
172     *
173     * @param index   The index for the overlay to set.
174     * @param overlay The overlay to set at the specified index location. May not be null.
175     * @return The image that was at the specified index location.
176     */
177    public BGFGImage setOverlay(int index, BGFGImage overlay) {
178        return _overlays.set(index, requireNonNull(overlay, "overlay == null"));
179    }
180
181    /**
182     * Adds the specified overlay to this canvas.
183     *
184     * @param overlay The overlay to add. May not be null.
185     */
186    public void addOverlay(BGFGImage overlay) {
187        _overlays.add(requireNonNull(overlay, "overlay == null"));
188    }
189
190    /**
191     * Removes the specified overlay from this canvas.
192     *
193     * @param index The index for the overlay to remove.
194     * @return The image that used to be at the specified index.
195     */
196    public BGFGImage removeOverlay(int index) {
197        return _overlays.remove(index);
198    }
199
200    /**
201     * Removes the specified overlay from this canvas.
202     *
203     * @param overlay The overlay image to remove from this canvas. May not be null.
204     * @return true if this canvas contained the specified overlay/foreground image.
205     */
206    public boolean removeOverlay(BGFGImage overlay) {
207        return _overlays.remove(requireNonNull(overlay, "overlay == null"));
208    }
209
210    /**
211     * Removes all the overlays from this canvas.
212     */
213    public void clearOverlays() {
214        _overlays.clear();
215    }
216
217    /**
218     * This routine is called by the Java 3D rendering loop after clearing the canvas and
219     * before any rendering has been done for this frame. This implementation renders any
220     * background images onto the canvas. Note that BGFGImage.getImage() is always called
221     * before getImageX() or getImageY().
222     */
223    @Override
224    public void preRender() {
225        super.preRender();
226        if (!_backgrounds.isEmpty()) {
227            synchronized (_backgrounds) {
228                for (BGFGImage background : _backgrounds) {
229                    //  Get the overlay image.
230                    BufferedImage bufim = background.getImage();
231                    if (isNull(bufim))
232                        continue;
233
234                    //  Get where the image should be drawn on the canvas.
235                    int x = background.getImageX();
236                    int y = background.getImageY();
237
238                    //  Draw and flush the image to the canvas.
239                    J3DGraphics2D j3dg2d = this.getGraphics2D();
240                    j3dg2d.drawAndFlushImage(bufim, x, y, this);
241                }
242            }
243        }
244    }
245
246    /**
247     * This routine is called by the Java 3D rendering loop after completing all rendering
248     * to the canvas for this frame and before the buffer swap. This implementation
249     * renders any overlay images onto the canvas. Note that BGFGImage.getImage() is
250     * always called before getImageX() or getImageY().
251     */
252    @Override
253    public void postRender() {
254        super.postRender();
255        if (!_overlays.isEmpty()) {
256            synchronized (_overlays) {
257                for (BGFGImage overlay : _overlays) {
258                    //  Get the overlay image.
259                    BufferedImage bufim = overlay.getImage();
260                    if (isNull(bufim))
261                        continue;
262
263                    //  Get where the image should be drawn on the canvas.
264                    int x = overlay.getImageX();
265                    int y = overlay.getImageY();
266
267                    //  Draw and flush the image to the canvas.
268                    J3DGraphics2D j3dg2d = this.getGraphics2D();
269                    j3dg2d.drawAndFlushImage(bufim, x, y, this);
270                }
271            }
272        }
273    }
274
275}