2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 package com.jme.system;
35 import java.util.Collection;
36 import java.util.HashMap;
37 import java.util.Iterator;
39 import java.util.logging.Logger;
41 import sun.misc.Service;
42 import sun.misc.ServiceConfigurationError;
44 import com.jme.image.Image;
45 import com.jme.input.joystick.JoystickInput;
46 import com.jme.math.Ray;
47 import com.jme.math.Vector2f;
48 import com.jme.math.Vector3f;
49 import com.jme.renderer.RenderContext;
50 import com.jme.renderer.Renderer;
51 import com.jme.renderer.TextureRenderer;
52 import com.jme.scene.state.RenderState;
53 import com.jme.system.canvas.CanvasConstructor;
54 import com.jme.system.canvas.JMECanvas;
55 import com.jme.system.dummy.DummySystemProvider;
56 import com.jme.system.jogl.JOGLSystemProvider;
57 import com.jme.system.lwjgl.LWJGLSystemProvider;
60 * <code>DisplaySystem</code> defines an interface for system creation.
61 * Specifically, any implementing class will create a window for rendering. It
62 * also should create the appropriate <code>Renderer</code> object that allows
63 * the client to render to this window. <p/> Implementing classes should check
64 * for the appropriate libraries to insure these libraries are indeed installed
65 * on the system. This will allow users to cleanly exit if an improper library
66 * was chosen for rendering. <p/> Example usage: <p/> <code>
67 * DisplaySystem ds = DisplaySystem.getDisplaySystem();<br>
68 * ds.createWindow(640,480,32,60,true);<br>
69 * Renderer r = ds.getRenderer();<br>
73 * @author Gregg Patton
74 * @author Joshua Slack - Optimizations, Headless rendering, RenderContexts, AWT integration
75 * @version $Id: DisplaySystem.java,v 1.67 2007/10/05 22:41:20 nca Exp $
76 * @see com.jme.renderer.Renderer
78 public abstract class DisplaySystem {
80 private static final Logger LOGGER = Logger.getLogger(DisplaySystem.class.getName());
82 /** The display system that has been created. */
83 private static volatile SystemProvider system;
86 * Width selected for the renderer.
91 * height selected for the renderer.
96 * Bit depth selected for renderer.
101 * Frequency selected for renderer.
106 * Is the display full screen?
108 protected boolean fs;
111 * Is the display created already?
113 protected boolean created;
116 * Alpha bits to use for the renderer.
118 protected int alphaBits = 0;
121 * Depth bits to use for the renderer.
123 protected int depthBits = 8;
126 * Stencil bits to use for the renderer.
128 protected int stencilBits = 0;
131 * Number of samples to use for the multisample buffer.
133 protected int samples = 0;
136 * Gamma value of display - default is 1.0f. 0->infinity
138 protected float gamma = 1.0f;
141 * Brightness value of display - default is 0f. -1.0 -> 1.0
143 protected float brightness = 0;
146 * Contrast value of display - default is 1.0f. 0->infinity
148 protected float contrast = 1;
150 private static final Map<String, SystemProvider> systemProviderMap = new HashMap<String, SystemProvider>();
152 private Map<String, Class<? extends CanvasConstructor>> canvasConstructRegistry = new HashMap<String, Class<? extends CanvasConstructor>>();
155 * A new display system has been created. The default static display system
156 * is set to the newly created display system.
158 protected DisplaySystem() {
162 * <code>getDisplaySystem</code> is a factory method that creates the
163 * appropriate display system specified by the key parameter. If the key
164 * given is not a valid identifier for a specific display system, the
165 * fallback default is returned.
168 * the display system to use.
169 * @return the appropriate display system specified by the key.
171 public static DisplaySystem getDisplaySystem(String key) {
172 // force to initialize joystick input before display system as there are
173 // LWJGL issues with creating it afterwards.
174 // FIXME What about the impact on other display systems?
178 setSystemProvider(getCachedSystemProvider(key));
180 catch (IllegalStateException alreadySet) {
181 LOGGER.warning(alreadySet.getMessage());
184 return getDisplaySystem();
187 private static SystemProvider getCachedSystemProvider(String providerId) {
188 return getSystemProviderMap().get(providerId);
191 private static Map<String, SystemProvider> getSystemProviderMap()
192 throws ServiceConfigurationError {
193 if (systemProviderMap.isEmpty()) {
194 @SuppressWarnings("unchecked")
195 Iterator<SystemProvider> displayProviders = Service.providers(SystemProvider.class);
196 while (displayProviders.hasNext()) {
197 SystemProvider provider = (SystemProvider) displayProviders
199 systemProviderMap.put(provider.getProviderIdentifier(),
203 // if the provider map is still empty (no providers found),
204 if (systemProviderMap.isEmpty()) {
205 // insert the default
206 SystemProvider sp = new LWJGLSystemProvider();
207 systemProviderMap.put(sp.getProviderIdentifier(), sp);
208 sp = new JOGLSystemProvider();
209 systemProviderMap.put(sp.getProviderIdentifier(), sp);
210 sp = new DummySystemProvider();
211 systemProviderMap.put(sp.getProviderIdentifier(), sp);
215 return systemProviderMap;
219 * Returns all available system providers
221 * @return String array containing all available system providers
223 public static String[] getSystemProviderIdentifiers() {
224 Collection<String> ids = getSystemProviderMap().keySet();
226 String[] names = new String[ids.size()];
234 * Returns the currently system provider. If no system provider has been
235 * set, then a default LWJGL system provider is used.
237 * @return The current system provider.
239 public static SystemProvider getSystemProvider() {
240 SystemProvider currentProvider = system;
241 if (currentProvider != null) {
242 return currentProvider;
245 // if none defined by Service.providers, use fallback default
246 synchronized (DisplaySystem.class) {
247 if (system == null) {
248 system = new LWJGLSystemProvider();
257 * Sets the SystemProvider to be provider.
259 * Once installed, the provider cannot be replaced.
262 * the SystemProvider to install. if <code>null</code>, no
264 * @throws IllegalStateException
265 * if a provider was previous installed.
269 public static synchronized void setSystemProvider(SystemProvider provider) throws IllegalStateException {
270 if (system != null) {
271 throw new IllegalStateException("SystemProvider already set");
278 * Returns the currently created display system.
280 * @return The current display system.
282 public static DisplaySystem getDisplaySystem() {
283 return getSystemProvider().getDisplaySystem();
287 * Sets a new width for the display system
290 public void setWidth(int width) {
295 * Returns the set width for the display system.
297 * @return The set width.
299 public int getWidth() {
304 * Sets a new height for the display system
307 public void setHeight(int height) {
308 this.height = height;
312 * Returns the set height for the display system.
314 * @return The set height.
316 public int getHeight() {
321 * Returns the set bitdepth for the display system.
323 * @return the set bit depth
325 public int getBitDepth() {
330 * Returns the set frequency for the display system.
332 * @return the set frequency
334 public int getFrequency() {
339 * Returns whether or not the display system is set to be full screen.
341 * @return true if full screen
343 public boolean isFullScreen() {
348 * <code>getAdapter</code> returns the name of the underlying system's
349 * graphics adapter for debugging / display purposes.
351 * @return the adapter's name as a String
353 public abstract String getAdapter();
356 * <code>getDriverVersion</code> returns a string representing the version
357 * of driver installed on the underlying system.
359 * @return the version as a String
361 public abstract String getDriverVersion();
364 * <code>getDisplayVendor</code> returns the vendor of the graphics
367 * @return The adapter vendor
369 public abstract String getDisplayVendor();
372 * <code>getDisplayRenderer</code> returns details of the adapter
374 * @return The adapter details
376 public abstract String getDisplayRenderer();
379 * <code>getDisplayAPIVersion</code> returns the API version supported
381 * @return The api version supported
383 public abstract String getDisplayAPIVersion();
386 * <code>isValidDisplayMode</code> determines if the given parameters
387 * constitute a valid display mode on this system. Returning true does not
388 * necessarily guarantee that the system is capable of running in the
389 * specified display mode, merely that it <i>believes </i> it is possible.
392 * the width/horizontal resolution of the display.
394 * the height/vertical resolution of the display.
396 * the bit depth of the display.
398 * the frequency of refresh of the display (in Hz).
400 public abstract boolean isValidDisplayMode(int width, int height, int bpp,
404 * <code>setVSyncEnabled</code> attempts to enable or disable monitor
405 * vertical synchronization. The method is a "best attempt" to change the
406 * monitor vertical refresh synchronization, and is <b>not </b> guaranteed
407 * to be successful. This is dependant on OS.
410 * <code>true</code> to synchronize, <code>false</code> to
411 * ignore synchronization
413 public abstract void setVSyncEnabled(boolean enabled);
416 * Sets the title of the display system. This is usually reflected by the
417 * renderer as text in the menu bar.
420 * The new display title.
422 public abstract void setTitle(String title);
425 * <code>createWindow</code> creates a window with the desired settings.
426 * The width and height defined by w and h define the size of the window if
427 * fullscreen is false, otherwise it defines the resolution of the
428 * fullscreen display. The color depth is defined by bpp. The implementing
429 * class should only allow 16, 24, and 32. The monitor frequency is defined
430 * by the frq parameter and should not exceed the capabilities of the
431 * connected hardware, the implementing class should attempt to assure this
432 * does not happen. Lastly, the boolean flag fs determines if the display
433 * should be windowed or fullscreen. If false, windowed is chosen. This
434 * window will be placed in the center of the screen initially. If true
435 * fullscreen mode will be entered with the appropriate settings.
438 * the width/horizontal resolution of the display.
440 * the height/vertical resolution of the display.
442 * the color depth of the display.
444 * the frequency of refresh of the display.
446 * flag determining if fullscreen is to be used or not. True will
447 * use fullscreen, false will use windowed mode.
449 public abstract void createWindow(int w, int h, int bpp, int frq, boolean fs);
452 * <code>createHeadlessWindow</code> creates a headless window with the
453 * desired settings. A headless window is a rendering target that is not
454 * shown on screen. It is useful for doing offline rendering, integration
455 * and so forth. You can not have a regular and headless window at the same
456 * time. The width and height defined by w and h define the size of the
457 * window. The color depth is defined by bpp.
460 * the width/horizontal resolution of the display.
462 * the height/vertical resolution of the display.
464 * the color depth of the display.
466 public abstract void createHeadlessWindow(int w, int h, int bpp);
469 * <code>createCanvas</code> should create a canvas object with the
470 * desired settings. The width and height defined by w and h define the size
471 * of the canvas. Makes an AWT canvas by default.
474 * the width/horizontal resolution of the display.
476 * the height/vertical resolution of the display.
478 public JMECanvas createCanvas(int w, int h) {
479 return createCanvas(w, h, "AWT", new HashMap<String, Object>());
483 * <code>createCanvas</code> should create a canvas object with the desired
484 * settings. The width and height defined by w and h define the size of the
488 * the width/horizontal resolution of the display.
490 * the height/vertical resolution of the display.
492 * the type of canvas to make. e.g. "AWT", "SWT".
494 * the properties we want to use (if any) for constructing our
497 public abstract JMECanvas createCanvas(int w, int h, String type, HashMap<String, Object> props);
499 public void registerCanvasConstructor(String type, Class<? extends CanvasConstructor> constructorClass) {
500 canvasConstructRegistry.put(type, constructorClass);
503 public CanvasConstructor makeCanvasConstructor(String type) {
504 Class<? extends CanvasConstructor> constructorClass = canvasConstructRegistry.get(type);
505 if (constructorClass == null) {
506 throw new JmeException("Unregistered canvas type: "+type);
508 CanvasConstructor constructor;
510 constructor = constructorClass.newInstance();
511 } catch (Exception e) {
512 throw new JmeException("Unable to instantiate canvas constructor: "+constructorClass);
518 * <code>recreateWindow</code> recreates a window with the desired
522 * the width/horizontal resolution of the display.
524 * the height/vertical resolution of the display.
526 * the color depth of the display.
528 * the frequency of refresh of the display.
530 * flag determining if fullscreen is to be used or not. True will
531 * use fullscreen, false will use windowed mode.
533 public abstract void recreateWindow(int w, int h, int bpp, int frq,
537 * <code>getRenderer</code> returns the <code>Renderer</code>
538 * implementation that is compatible with the chosen
539 * <code>DisplaySystem</code>. For example, if
540 * <code>LWJGLDisplaySystem</code> is used, the returned
541 * <code>Renderer</code> will be</code> LWJGLRenderer</code>.
543 * @return the appropriate <code>Renderer</code> implementation that is
544 * compatible with the used <code>DisplaySystem</code>.
545 * @see com.jme.renderer.Renderer
547 public abstract Renderer getRenderer();
550 * <code>setRenderer</code> sets the <code>Renderer</code> object that
551 * is to be used by this display. The implementing class should take
552 * measures to insure that the given Renderer is compatible with the
556 * the Renderer to set for this display.
558 public abstract void setRenderer(Renderer r);
561 * <code>isCreated</code> returns the current status of the display
562 * system. If the window and renderer are created, true is returned,
565 * @return whether the display system is created.
567 public boolean isCreated() {
572 * <code>isActive</code> returns true if the display is active.
574 * @return whether the display system is active.
576 public abstract boolean isActive();
580 * <code>isClosing</code> notifies if the window is currently closing.
581 * This could be caused via the application itself or external interrupts
582 * such as alt-f4 etc.
584 * @return true if the window is closing, false otherwise.
586 public abstract boolean isClosing();
589 * <code>reset</code> cleans up the display system for closing or
592 public abstract void reset();
595 * <code>close</code> shutdowns and destroys any window contexts.
597 public abstract void close();
600 * Returns the minimum bits per pixel in the alpha buffer.
602 * @return the int value of alphaBits.
604 public int getMinAlphaBits() {
609 * Sets the minimum bits per pixel in the alpha buffer.
612 * the new value for alphaBits
614 public void setMinAlphaBits(int alphaBits) {
615 this.alphaBits = alphaBits;
619 * Returns the minimum bits per pixel in the depth buffer.
621 * @return the int value of depthBits.
623 public int getMinDepthBits() {
628 * Sets the minimum bits per pixel in the depth buffer.
631 * the new value for depthBits
633 public void setMinDepthBits(int depthBits) {
634 this.depthBits = depthBits;
638 * Returns the minimum bits per pixel in the stencil buffer.
640 * @return the int value of stencilBits.
642 public int getMinStencilBits() {
647 * Sets the minimum bits per pixel in the stencil buffer.
649 * @param stencilBits -
650 * the new value for stencilBits
652 public void setMinStencilBits(int stencilBits) {
653 this.stencilBits = stencilBits;
657 * Returns the minimum samples in multisample buffer.
659 * @return the int value of samples.
661 public int getMinSamples() {
666 * Sets the minimum samples in the multisample buffer.
669 * the new value for samples
671 public void setMinSamples(int samples) {
672 this.samples = samples;
676 * Returns the brightness last requested by this display.
678 * @return brightness - should be between -1 and 1.
680 public float getBrightness() {
685 * Note: This affects the whole screen, not just the game window.
688 * The brightness to set (set -1 to 1) default is 0
690 public void setBrightness(float brightness) {
691 this.brightness = brightness;
696 * @return Returns the contrast.
698 public float getContrast() {
703 * Note: This affects the whole screen, not just the game window.
706 * The contrast to set (set greater than 0) default is 1
708 public void setContrast(float contrast) {
709 this.contrast = contrast;
714 * @return Returns the gamma.
716 public float getGamma() {
721 * Note: This affects the whole screen, not just the game window.
724 * The gamma to set (default is 1)
726 public void setGamma(float gamma) {
732 * Sets all three in one call. <p/> Note: This affects the whole screen, not
733 * just the game window.
739 public void setBrightnessGammaContrast(float brightness, float gamma,
741 this.brightness = brightness;
743 this.contrast = contrast;
748 * Called when the display system is created, this function sets the default
749 * render states for the renderer. It should not be called directly by the
753 * The renderer to get the default states from.
755 public static void updateStates(Renderer r) {
757 for (RenderState.StateType type : RenderState.StateType.values()) {
759 Renderer.defaultStateList[type.ordinal()] = r.createState(type);
760 Renderer.defaultStateList[type.ordinal()].setEnabled(false);
765 * Create a TextureRenderer using the underlying system.
772 * @return A TextureRenderer for the display system.
774 public abstract TextureRenderer createTextureRenderer(int width,
775 int height, TextureRenderer.Target target);
778 * Translate world to screen coordinates
780 * @param worldPosition
781 * Vector3f representing the world position to retrieve.
782 * @return the screen position.
784 public Vector3f getScreenCoordinates(Vector3f worldPosition) {
785 return getScreenCoordinates(worldPosition, null);
789 * Translate world to screen coordinates
791 * @param worldPosition
792 * Vector3f representing the world position to retrieve.
794 * Vector3f to store the world position in.
795 * @return Vector3f The store vector3f, after storing.
797 public Vector3f getScreenCoordinates(Vector3f worldPosition, Vector3f store) {
798 return getRenderer().getCamera().getScreenCoordinates(worldPosition,
803 * Translate screen to world coordinates.
805 * @param screenPosition
806 * Vector2f representing the screen position with 0,0 at the
809 * The z position away from the viewing plane, between 0 and 1.
810 * @return A Vector3f representing the vector's world position.
812 public Vector3f getWorldCoordinates(Vector2f screenPosition, float zPos) {
813 return getWorldCoordinates(screenPosition, zPos, null);
817 * Translate screen to world coordinates.
819 * @param screenPosition
820 * Vector2f representing the screen position with 0,0 at the
823 * float The z position away from the viewing plane.
825 * Vector3f The vector to store the result in.
826 * @return Vector3f The store vector, after storing it's result.
828 public Vector3f getWorldCoordinates(Vector2f screenPosition, float zPos,
830 return getRenderer().getCamera().getWorldCoordinates(screenPosition,
835 * Generate a pick ray from a 2d screen point. The screen point is assumed
836 * to have origin at the lower left, but when using awt mouse clicks, you'll
837 * want to set flipVertical to true since that system has an origin at the
838 * upper right. The Ray will be in world coordinates and the direction will
841 * @param screenPosition
842 * Vector2f representing the screen position with 0,0 at the
844 * @param flipVertical
845 * Whether or not to flip the y coordinate of the screen position
846 * across the middle of the screen.
848 * The ray to store the result in. If null, a new Ray is created.
851 public Ray getPickRay(Vector2f screenPosition, boolean flipVertical,
854 screenPosition.y = getRenderer().getHeight() - screenPosition.y;
856 if (store == null) store = new Ray();
857 getWorldCoordinates(screenPosition, 0,
859 getWorldCoordinates(screenPosition, 0.3f,
860 store.direction).subtractLocal(store.origin)
866 * Update the display's gamma, brightness and contrast based on the set
869 protected abstract void updateDisplayBGC();
872 * Sets one or more icons for the DisplaySystem.
874 * As a reference for usual platforms on number of icons and their sizes:
876 * <li>On Windows you should supply at least one 16x16 image and one 32x32.</li>
877 * <li>Linux (and similar platforms) expect one 32x32 image.</li>
878 * <li>Mac OS X should be supplied one 128x128 image.</li>
882 * Images should be in format RGBA8888. If they are not jME will try to convert them
883 * using ImageUtils. If that fails a <code>JmeException</code> could be thrown.
887 * Array of Images to be used as icons.
889 * @author Tijl Houtbeckers - some changes to handling non-RGBA8888 Images.
892 public abstract void setIcon(Image[] iconImages);
895 * @return a RenderContext object representing the current OpenGL context.
897 public abstract RenderContext<?> getCurrentContext();
899 public static synchronized void resetSystemProvider() {
900 if (system != null) {
901 system.disposeDisplaySystem();
907 * If running in windowed mode, move the window's position to the given
908 * display coordinates.
913 public abstract void moveWindowTo(int locX, int locY);