From 9dd02f103042cb8a196f8a3ed2278da443e345bf Mon Sep 17 00:00:00 2001 From: neothemachine Date: Wed, 5 Dec 2012 17:03:16 +0100 Subject: move all files from trunk to root folder --- .../ardor3d/framework/lwjgl/LwjglAwtCanvas.java | 110 ++ .../com/ardor3d/framework/lwjgl/LwjglCanvas.java | 280 ++++ .../framework/lwjgl/LwjglCanvasCallback.java | 31 + .../framework/lwjgl/LwjglCanvasRenderer.java | 208 +++ .../framework/lwjgl/LwjglDisplayCanvas.java | 137 ++ .../framework/lwjgl/LwjglHeadlessCanvas.java | 257 +++ .../ardor3d/framework/lwjgl/LwjglLibraryPaths.java | 80 + .../input/jinput/JInputControllerWrapper.java | 125 ++ .../input/lwjgl/LwjglControllerWrapper.java | 124 ++ .../java/com/ardor3d/input/lwjgl/LwjglKey.java | 173 ++ .../ardor3d/input/lwjgl/LwjglKeyboardWrapper.java | 64 + .../com/ardor3d/input/lwjgl/LwjglMouseManager.java | 142 ++ .../com/ardor3d/input/lwjgl/LwjglMouseWrapper.java | 144 ++ .../renderer/lwjgl/LwjglContextCapabilities.java | 237 +++ .../lwjgl/LwjglPbufferTextureRenderer.java | 397 +++++ .../com/ardor3d/renderer/lwjgl/LwjglRenderer.java | 1743 ++++++++++++++++++++ .../renderer/lwjgl/LwjglTextureRenderer.java | 566 +++++++ .../lwjgl/LwjglTextureRendererProvider.java | 48 + .../scene/state/lwjgl/LwjglBlendStateUtil.java | 425 +++++ .../scene/state/lwjgl/LwjglClipStateUtil.java | 68 + .../scene/state/lwjgl/LwjglColorMaskStateUtil.java | 43 + .../scene/state/lwjgl/LwjglCullStateUtil.java | 93 ++ .../scene/state/lwjgl/LwjglFogStateUtil.java | 153 ++ .../state/lwjgl/LwjglFragmentProgramStateUtil.java | 113 ++ .../scene/state/lwjgl/LwjglLightStateUtil.java | 372 +++++ .../scene/state/lwjgl/LwjglMaterialStateUtil.java | 199 +++ .../scene/state/lwjgl/LwjglOffsetStateUtil.java | 85 + .../state/lwjgl/LwjglShaderObjectsStateUtil.java | 361 ++++ .../scene/state/lwjgl/LwjglShadingStateUtil.java | 52 + .../scene/state/lwjgl/LwjglStencilStateUtil.java | 186 +++ .../scene/state/lwjgl/LwjglTextureStateUtil.java | 1686 +++++++++++++++++++ .../state/lwjgl/LwjglVertexProgramStateUtil.java | 123 ++ .../scene/state/lwjgl/LwjglWireframeStateUtil.java | 83 + .../scene/state/lwjgl/LwjglZBufferStateUtil.java | 90 + .../scene/state/lwjgl/shader/LwjglShaderUtil.java | 400 +++++ .../scene/state/lwjgl/util/LwjglRendererUtil.java | 99 ++ .../scene/state/lwjgl/util/LwjglTextureUtil.java | 633 +++++++ .../src/main/native/linux/libjinput-linux.so | Bin 0 -> 10604 bytes .../src/main/native/linux/libjinput-linux64.so | Bin 0 -> 13776 bytes ardor3d-lwjgl/src/main/native/linux/liblwjgl.so | Bin 0 -> 277580 bytes ardor3d-lwjgl/src/main/native/linux/liblwjgl64.so | Bin 0 -> 346360 bytes ardor3d-lwjgl/src/main/native/linux/libopenal.so | Bin 0 -> 158152 bytes ardor3d-lwjgl/src/main/native/linux/libopenal64.so | Bin 0 -> 177410 bytes .../src/main/native/macosx/libjinput-osx.jnilib | Bin 0 -> 49016 bytes .../src/main/native/macosx/liblwjgl.jnilib | Bin 0 -> 841968 bytes ardor3d-lwjgl/src/main/native/solaris/liblwjgl.so | Bin 0 -> 247112 bytes ardor3d-lwjgl/src/main/native/solaris/libopenal.so | Bin 0 -> 253928 bytes ardor3d-lwjgl/src/main/native/win32/OpenAL32.dll | Bin 0 -> 103936 bytes ardor3d-lwjgl/src/main/native/win32/jinput-dx8.dll | Bin 0 -> 31232 bytes ardor3d-lwjgl/src/main/native/win32/jinput-raw.dll | Bin 0 -> 29184 bytes ardor3d-lwjgl/src/main/native/win32/lwjgl.dll | Bin 0 -> 153600 bytes 51 files changed, 10130 insertions(+) create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglAwtCanvas.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvas.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasCallback.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasRenderer.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglDisplayCanvas.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglHeadlessCanvas.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglLibraryPaths.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/input/jinput/JInputControllerWrapper.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglControllerWrapper.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKey.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKeyboardWrapper.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseManager.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseWrapper.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglContextCapabilities.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglPbufferTextureRenderer.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglRenderer.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRenderer.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRendererProvider.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglBlendStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglClipStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglColorMaskStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglCullStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFogStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFragmentProgramStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglLightStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglMaterialStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglOffsetStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShaderObjectsStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShadingStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglStencilStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglTextureStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglVertexProgramStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglWireframeStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglZBufferStateUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/shader/LwjglShaderUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglRendererUtil.java create mode 100644 ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglTextureUtil.java create mode 100644 ardor3d-lwjgl/src/main/native/linux/libjinput-linux.so create mode 100644 ardor3d-lwjgl/src/main/native/linux/libjinput-linux64.so create mode 100644 ardor3d-lwjgl/src/main/native/linux/liblwjgl.so create mode 100644 ardor3d-lwjgl/src/main/native/linux/liblwjgl64.so create mode 100644 ardor3d-lwjgl/src/main/native/linux/libopenal.so create mode 100644 ardor3d-lwjgl/src/main/native/linux/libopenal64.so create mode 100644 ardor3d-lwjgl/src/main/native/macosx/libjinput-osx.jnilib create mode 100644 ardor3d-lwjgl/src/main/native/macosx/liblwjgl.jnilib create mode 100644 ardor3d-lwjgl/src/main/native/solaris/liblwjgl.so create mode 100644 ardor3d-lwjgl/src/main/native/solaris/libopenal.so create mode 100644 ardor3d-lwjgl/src/main/native/win32/OpenAL32.dll create mode 100644 ardor3d-lwjgl/src/main/native/win32/jinput-dx8.dll create mode 100644 ardor3d-lwjgl/src/main/native/win32/jinput-raw.dll create mode 100644 ardor3d-lwjgl/src/main/native/win32/lwjgl.dll (limited to 'ardor3d-lwjgl/src/main') diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglAwtCanvas.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglAwtCanvas.java new file mode 100644 index 0000000..e3387f2 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglAwtCanvas.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.framework.lwjgl; + +import java.util.concurrent.CountDownLatch; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.AWTGLCanvas; +import org.lwjgl.opengl.PixelFormat; + +import com.ardor3d.annotation.MainThread; +import com.ardor3d.framework.Canvas; +import com.ardor3d.framework.DisplaySettings; + +public class LwjglAwtCanvas extends AWTGLCanvas implements Canvas { + + private static final long serialVersionUID = 1L; + + private final LwjglCanvasRenderer _canvasRenderer; + private boolean _inited = false; + private final DisplaySettings _settings; + + private volatile boolean _updated = false; + // _latch would have to be volatile if we are not careful with the order of reads and writes between this one and + // '_updated' + private CountDownLatch _latch = null; + + public LwjglAwtCanvas(final DisplaySettings settings, final LwjglCanvasRenderer canvasRenderer) + throws LWJGLException { + super(new PixelFormat(settings.getColorDepth(), settings.getAlphaBits(), settings.getDepthBits(), settings + .getStencilBits(), settings.getSamples()).withStereo(settings.isStereo())); + _settings = settings; + _canvasRenderer = canvasRenderer; + _canvasRenderer.setCanvasCallback(new LwjglCanvasCallback() { + @Override + public void makeCurrent() throws LWJGLException { + LwjglAwtCanvas.this.makeCurrent(); + } + + @Override + public void releaseContext() throws LWJGLException { + LwjglAwtCanvas.this.releaseContext(); + } + }); + } + + public void draw(final CountDownLatch latch) { + if (!shouldDraw(latch)) { + latch.countDown(); + return; + } + + // need to set _latch before _updated, for memory consistency reasons + _latch = latch; + _updated = true; + repaint(); + } + + private boolean shouldDraw(final CountDownLatch latch) { + final boolean showing = isShowing(); + final boolean lastUpdateComplete = latch == null || !_updated; + return showing && lastUpdateComplete; + } + + @Override + @MainThread + protected void paintGL() { + if (!_inited) { + _canvasRenderer.init(_settings, false); // false - do not do back buffer swap, awt will do that. + _canvasRenderer.getCamera().resize(getWidth(), getHeight()); + _inited = true; + } + + if (_latch != null && !_updated) { + return; + } + + try { + if (_canvasRenderer.draw()) { + swapBuffers(); + } + } catch (final LWJGLException e) { + throw new RuntimeException(e); + } finally { + // release our context - because swap is external, we release here instead. + _canvasRenderer.releaseCurrentContext(); + } + + if (_latch != null) { + _updated = false; + _latch.countDown(); + } + } + + public void init() { + ; // ignore - can only be inited inside our paintGL + } + + public LwjglCanvasRenderer getCanvasRenderer() { + return _canvasRenderer; + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvas.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvas.java new file mode 100644 index 0000000..7fe8b9f --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvas.java @@ -0,0 +1,280 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.framework.lwjgl; + +import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.lwjgl.opengl.PixelFormat; + +import com.ardor3d.annotation.MainThread; +import com.ardor3d.framework.CanvasRenderer; +import com.ardor3d.framework.DisplaySettings; +import com.ardor3d.framework.NativeCanvas; +import com.ardor3d.image.Image; +import com.ardor3d.image.ImageDataFormat; +import com.ardor3d.image.PixelDataType; +import com.ardor3d.input.FocusWrapper; +import com.ardor3d.util.Ardor3dException; +import com.ardor3d.util.geom.BufferUtils; + +/** + * A canvas implementation for use with native LWJGL windows. + */ +public class LwjglCanvas implements NativeCanvas, FocusWrapper { + private static final Logger logger = Logger.getLogger(LwjglCanvas.class.getName()); + + private final LwjglCanvasRenderer _canvasRenderer; + + private final DisplaySettings _settings; + private boolean _inited = false; + + private volatile boolean _focusLost = false; + + /** + * If true, we will not try to drop and reclaim the context on each frame. + */ + public static boolean SINGLE_THREADED_MODE = true; + + public LwjglCanvas(final DisplaySettings settings, final LwjglCanvasRenderer canvasRenderer) { + _canvasRenderer = canvasRenderer; + _canvasRenderer.setCanvasCallback(new LwjglCanvasCallback() { + @Override + public void makeCurrent() throws LWJGLException { + if (!SINGLE_THREADED_MODE) { + Display.makeCurrent(); + } + } + + @Override + public void releaseContext() throws LWJGLException { + if (!SINGLE_THREADED_MODE) { + Display.releaseContext(); + } + } + }); + _settings = settings; + } + + public boolean getAndClearFocusLost() { + final boolean result = _focusLost; + + _focusLost = false; + + return result; + } + + @MainThread + // hm, seem to have to control the order of initialization: display first, then keyboard/etc, in native windows + public void init() { + if (_inited) { + return; + } + + // create the Display. + DisplayMode mode; + if (_settings.isFullScreen()) { + mode = getValidDisplayMode(_settings); + if (null == mode) { + throw new Ardor3dException("Bad display mode (w/h/bpp/freq): " + _settings.getWidth() + " / " + + _settings.getHeight() + " / " + _settings.getColorDepth() + " / " + _settings.getFrequency()); + } + } else { + mode = new DisplayMode(_settings.getWidth(), _settings.getHeight()); + } + + final PixelFormat format = new PixelFormat(_settings.getAlphaBits(), _settings.getDepthBits(), + _settings.getStencilBits()).withSamples(_settings.getSamples()).withStereo(_settings.isStereo()); + + try { + Display.setDisplayMode(mode); + Display.setFullscreen(_settings.isFullScreen()); + Display.create(format); + } catch (final Exception e) { + logger.severe("Cannot create window"); + logger.logp(Level.SEVERE, this.getClass().toString(), "initDisplay()", "Exception", e); + throw new Ardor3dException("Cannot create window: " + e.getMessage()); + } + + _canvasRenderer.init(_settings, true); // true - do swap in renderer. + _inited = true; + } + + @MainThread + public void draw(final CountDownLatch latch) { + if (!_inited) { + init(); + } + + checkFocus(); + + _canvasRenderer.draw(); + + if (latch != null) { + latch.countDown(); + } + } + + private void checkFocus() { + // focusLost should be true if it is already true (hasn't been read/cleared yet), or + // the display is presently not in focus + _focusLost = _focusLost || !(Display.isActive() && Display.isVisible()); + + // + // final boolean newFocus = + // + // if (!focusLost && newFocus) { + // // didn't use to have focus, but now we do + // // do nothing for now, just keep track of the fact that we have focus + // focusLost = newFocus; + // } else if (focusLost && !newFocus) { + // // had focus, but don't anymore - notify the physical input layer + // physicalLayer.lostFocus(); + // focusLost = newFocus; + // } + } + + public CanvasRenderer getCanvasRenderer() { + return _canvasRenderer; + } + + /** + * @return a DisplayMode object that has the requested settings. If there is no mode that supports a + * requested resolution, null is returned. + */ + private DisplayMode getValidDisplayMode(final DisplaySettings settings) { + // get all the modes, and find one that matches our settings. + DisplayMode[] modes; + try { + modes = Display.getAvailableDisplayModes(); + } catch (final LWJGLException e) { + logger.logp(Level.SEVERE, this.getClass().toString(), "getValidDisplayMode(width, height, bpp, freq)", + "Exception", e); + return null; + } + + // Try to find a best match. + int best_match = -1; // looking for request size/bpp followed by exact or highest freq + int match_freq = -1; + for (int i = 0; i < modes.length; i++) { + if (modes[i].getWidth() != settings.getWidth()) { + logger.fine("DisplayMode " + modes[i] + ": Width != " + settings.getWidth()); + continue; + } + if (modes[i].getHeight() != settings.getHeight()) { + logger.fine("DisplayMode " + modes[i] + ": Height != " + settings.getHeight()); + continue; + } + if (settings.getColorDepth() != 0 && modes[i].getBitsPerPixel() != settings.getColorDepth()) { + // should pick based on best match here too + logger.fine("DisplayMode " + modes[i] + ": Bits per pixel != " + settings.getColorDepth()); + continue; + } + if (best_match == -1) { + logger.fine("DisplayMode " + modes[i] + ": Match! "); + best_match = i; + match_freq = modes[i].getFrequency(); + } else { + final int cur_freq = modes[i].getFrequency(); + if (match_freq != settings.getFrequency() && // Previous is not a perfect match + (cur_freq == settings.getFrequency() || // Current is perfect match + match_freq < cur_freq)) // or is higher freq + { + logger.fine("DisplayMode " + modes[i] + ": Better match!"); + best_match = i; + match_freq = cur_freq; + } + } + } + + if (best_match == -1) { + return null; // none found; + } else { + logger.info("Selected DisplayMode: " + modes[best_match]); + return modes[best_match]; + } + } + + public void close() { + if (Display.isCreated()) { + Display.destroy(); + } + } + + public boolean isActive() { + return Display.isCreated() && Display.isActive(); + } + + @MainThread + public boolean isClosing() { + return Display.isCreated() && Display.isCloseRequested(); + } + + public void moveWindowTo(final int locX, final int locY) { + if (Display.isCreated()) { + Display.setLocation(locX, locY); + } + } + + public void setIcon(final Image[] iconImages) { + final ByteBuffer[] iconData = new ByteBuffer[iconImages.length]; + for (int i = 0; i < iconData.length; i++) { + // Image.Format.RGBA8 is the format that LWJGL requires, so try to convert if it's not. + if (iconImages[i].getDataType() != PixelDataType.UnsignedByte) { + throw new Ardor3dException( + "Your icon is in a format that could not be converted to UnsignedByte - RGBA"); + } + + if (iconImages[i].getDataFormat() != ImageDataFormat.RGBA) { + if (iconImages[i].getDataFormat() != ImageDataFormat.RGB) { + throw new Ardor3dException( + "Your icon is in a format that could not be converted to UnsignedByte - RGBA"); + } + iconImages[i] = _RGB888_to_RGBA8888(iconImages[i]); + } + + iconData[i] = iconImages[i].getData(0); + iconData[i].rewind(); + } + Display.setIcon(iconData); + } + + private static Image _RGB888_to_RGBA8888(final Image rgb888) { + final int size = rgb888.getWidth() * rgb888.getHeight() * 4; + + final ByteBuffer rgb = rgb888.getData(0); + + final ByteBuffer rgba8888 = BufferUtils.createByteBuffer(size); + rgb.rewind(); + for (int j = 0; j < size; j++) { + if ((j + 1) % 4 == 0) { + rgba8888.put((byte) 0xFF); + } else { + rgba8888.put(rgb.get()); + } + } + return new Image(ImageDataFormat.RGBA, PixelDataType.UnsignedByte, rgb888.getWidth(), rgb888.getHeight(), + rgba8888, null); + } + + public void setTitle(final String title) { + Display.setTitle(title); + } + + public void setVSyncEnabled(final boolean enabled) { + Display.setVSyncEnabled(enabled); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasCallback.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasCallback.java new file mode 100644 index 0000000..a65c7e2 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasCallback.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.framework.lwjgl; + +import org.lwjgl.LWJGLException; + +public interface LwjglCanvasCallback { + + /** + * Request this canvas as the current opengl owner. + * + * @throws LWJGLException + */ + void makeCurrent() throws LWJGLException; + + /** + * Release this canvas as the current opengl owner. + * + * @throws LWJGLException + */ + void releaseContext() throws LWJGLException; + +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasRenderer.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasRenderer.java new file mode 100644 index 0000000..ab66a1d --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglCanvasRenderer.java @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.framework.lwjgl; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.ARBMultisample; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GLContext; + +import com.ardor3d.annotation.MainThread; +import com.ardor3d.framework.CanvasRenderer; +import com.ardor3d.framework.DisplaySettings; +import com.ardor3d.framework.Scene; +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.Vector3; +import com.ardor3d.renderer.Camera; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.Camera.ProjectionMode; +import com.ardor3d.renderer.lwjgl.LwjglContextCapabilities; +import com.ardor3d.renderer.lwjgl.LwjglRenderer; +import com.ardor3d.util.Ardor3dException; + +public class LwjglCanvasRenderer implements CanvasRenderer { + protected Scene _scene; + protected Camera _camera; + protected boolean _doSwap; + protected LwjglRenderer _renderer; + protected Object _context = new Object(); + protected int _frameClear = Renderer.BUFFER_COLOR_AND_DEPTH; + + private RenderContext _currentContext; + private LwjglCanvasCallback _canvasCallback; + + // NOTE: This code commented out by Petter 090224, since it isn't really ready to be used, + // and since it is at the moment more work than it is worth to get it ready. Later on, when + // we have solved some more fundamental problems, it is probably time to revisit this. + + // ensure availability of LWJGL natives + // { + // final String[] libraryPaths = LwjglLibraryPaths.getLibraryPaths(System.getProperty("os.name"), System + // .getProperty("os.arch")); + // + // try { + // NativeLoader.makeLibrariesAvailable(libraryPaths); + // } catch (final Exception e) { + // ; // ignore + // } + // } + + public LwjglCanvasRenderer(final Scene scene) { + _scene = scene; + } + + @MainThread + protected ContextCapabilities createContextCapabilities() { + return new LwjglContextCapabilities(GLContext.getCapabilities()); + } + + @MainThread + public void init(final DisplaySettings settings, final boolean doSwap) { + _doSwap = doSwap; + + // Look up a shared context, if a shared LwjglCanvasRenderer is given. + // XXX: Shared contexts will probably not work... lwjgl does not seem to have a way to make a new glcontext that + // shares lists, textures, etc. + RenderContext sharedContext = null; + if (settings.getShareContext() != null) { + sharedContext = ContextManager.getContextForKey(settings.getShareContext().getRenderContext() + .getContextKey()); + } + + try { + _canvasCallback.makeCurrent(); + GLContext.useContext(_context); + } catch (final LWJGLException e) { + throw new Ardor3dException("Unable to init CanvasRenderer.", e); + } + + final ContextCapabilities caps = createContextCapabilities(); + _currentContext = new RenderContext(this, caps, sharedContext); + + ContextManager.addContext(this, _currentContext); + ContextManager.switchContext(this); + + _renderer = new LwjglRenderer(); + + if (settings.getSamples() != 0 && caps.isMultisampleSupported()) { + GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB); + } + + _renderer.setBackgroundColor(ColorRGBA.BLACK); + + if (_camera == null) { + /** Set up how our camera sees. */ + _camera = new Camera(settings.getWidth(), settings.getHeight()); + _camera.setFrustumPerspective(45.0f, (float) settings.getWidth() / (float) settings.getHeight(), 1, 1000); + _camera.setProjectionMode(ProjectionMode.Perspective); + + final Vector3 loc = new Vector3(0.0f, 0.0f, 10.0f); + final Vector3 left = new Vector3(-1.0f, 0.0f, 0.0f); + final Vector3 up = new Vector3(0.0f, 1.0f, 0.0f); + final Vector3 dir = new Vector3(0.0f, 0f, -1.0f); + /** Move our camera to a correct place and orientation. */ + _camera.setFrame(loc, left, up, dir); + } else { + // use new width and height to set ratio. + _camera.setFrustumPerspective(_camera.getFovY(), + (float) settings.getWidth() / (float) settings.getHeight(), _camera.getFrustumNear(), _camera + .getFrustumFar()); + } + } + + @MainThread + public boolean draw() { + + // set up context for rendering this canvas + makeCurrentContext(); + + // render stuff, first apply our camera if we have one + if (_camera != null) { + if (Camera.getCurrentCamera() != _camera) { + _camera.update(); + } + _camera.apply(_renderer); + } + _renderer.clearBuffers(_frameClear); + + final boolean drew = _scene.renderUnto(_renderer); + _renderer.flushFrame(drew && _doSwap); + + // release the context if we're done (swapped and all) + if (_doSwap) { + releaseCurrentContext(); + } + + return drew; + } + + public Camera getCamera() { + return _camera; + } + + public Scene getScene() { + return _scene; + } + + public void setScene(final Scene scene) { + _scene = scene; + } + + public LwjglCanvasCallback getCanvasCallback() { + return _canvasCallback; + } + + public void setCanvasCallback(final LwjglCanvasCallback canvasCallback) { + _canvasCallback = canvasCallback; + } + + public Renderer getRenderer() { + return _renderer; + } + + public void makeCurrentContext() throws Ardor3dException { + try { + _canvasCallback.makeCurrent(); + GLContext.useContext(_context); + ContextManager.switchContext(this); + } catch (final LWJGLException e) { + throw new Ardor3dException("Failed to claim OpenGL context.", e); + } + } + + public void releaseCurrentContext() { + try { + GLContext.useContext(null); + _canvasCallback.releaseContext(); + } catch (final LWJGLException e) { + throw new RuntimeException(e); + } + } + + public void setCamera(final Camera camera) { + _camera = camera; + } + + public RenderContext getRenderContext() { + return _currentContext; + } + + public int getFrameClear() { + return _frameClear; + } + + public void setFrameClear(final int buffers) { + _frameClear = buffers; + } +} \ No newline at end of file diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglDisplayCanvas.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglDisplayCanvas.java new file mode 100644 index 0000000..efacc46 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglDisplayCanvas.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.framework.lwjgl; + +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.PixelFormat; + +import com.ardor3d.framework.Canvas; +import com.ardor3d.framework.DisplaySettings; +import com.ardor3d.input.FocusWrapper; +import com.ardor3d.util.Ardor3dException; + +/** + * A Canvas implementation intended for use with an existing awt canvas. + *

+ * XXX: Could/should this be merged with LwjglCanvas? + *

+ */ +public class LwjglDisplayCanvas implements Canvas, FocusWrapper { + private static final Logger logger = Logger.getLogger(LwjglDisplayCanvas.class.getName()); + + private final LwjglCanvasRenderer _canvasRenderer; + private boolean _inited = false; + private final DisplaySettings _settings; + + private volatile boolean _focusLost = false; + + private final java.awt.Canvas _canvas; + + public LwjglDisplayCanvas(final java.awt.Canvas canvas, final DisplaySettings settings, + final LwjglCanvasRenderer canvasRenderer) throws LWJGLException { + _settings = settings; + _canvasRenderer = canvasRenderer; + _canvas = canvas; + _canvasRenderer.setCanvasCallback(new LwjglCanvasCallback() { + @Override + public void makeCurrent() throws LWJGLException { + if (!LwjglCanvas.SINGLE_THREADED_MODE) { + Display.makeCurrent(); + } + } + + @Override + public void releaseContext() throws LWJGLException { + if (!LwjglCanvas.SINGLE_THREADED_MODE) { + Display.releaseContext(); + } + } + }); + } + + public void draw(final CountDownLatch latch) { + if (!_inited) { + init(); + } + + checkFocus(); + + _canvasRenderer.draw(); + + if (latch != null) { + latch.countDown(); + } + } + + private void checkFocus() { + // focusLost should be true if it is already true (hasn't been read/cleared yet), or + // the display is presently not in focus + _focusLost = _focusLost || !(Display.isActive() && Display.isVisible()); + } + + public boolean getAndClearFocusLost() { + final boolean result = _focusLost; + + _focusLost = false; + + return result; + } + + public void init() { + if (_inited) { + return; + } + + // create the Display. + final PixelFormat format = new PixelFormat(_settings.getAlphaBits(), _settings.getDepthBits(), + _settings.getStencilBits()).withSamples(_settings.getSamples()).withStereo(_settings.isStereo()); + + try { + Display.setParent(_canvas); + // NOTE: Workaround for possible lwjgl "pixel not accelerated" bug, as suggested by user "faust" + try { + Display.create(format); + } catch (final LWJGLException e) { + // failed to create Display, apply workaround (sleep for 1 second) and try again + Thread.sleep(1000); + Display.create(format); + } + } catch (final Exception e) { + logger.severe("Cannot create window"); + logger.logp(Level.SEVERE, this.getClass().toString(), "initDisplay()", "Exception", e); + throw new Ardor3dException("Cannot create window: " + e.getMessage()); + } + + _canvasRenderer.init(_settings, true); // true - do swap in renderer. + _inited = true; + } + + public LwjglCanvasRenderer getCanvasRenderer() { + return _canvasRenderer; + } + + public void setVSyncEnabled(final boolean enabled) { + Display.setVSyncEnabled(enabled); + } + + public void setFullScreen(final boolean fullScreen) throws LWJGLException { + Display.setFullscreen(fullScreen); + } + + public boolean isFullScreen() { + return Display.isFullscreen(); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglHeadlessCanvas.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglHeadlessCanvas.java new file mode 100644 index 0000000..26f8d78 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglHeadlessCanvas.java @@ -0,0 +1,257 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.framework.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.ARBMultisample; +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GLContext; +import org.lwjgl.opengl.Pbuffer; +import org.lwjgl.opengl.PixelFormat; + +import com.ardor3d.framework.DisplaySettings; +import com.ardor3d.framework.Scene; +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.Vector3; +import com.ardor3d.renderer.Camera; +import com.ardor3d.renderer.Camera.ProjectionMode; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.lwjgl.LwjglContextCapabilities; +import com.ardor3d.renderer.lwjgl.LwjglRenderer; +import com.ardor3d.renderer.lwjgl.LwjglTextureRenderer; +import com.ardor3d.util.Ardor3dException; +import com.ardor3d.util.Constants; +import com.ardor3d.util.geom.BufferUtils; + +/** + *

+ * A "canvas" class for use in drawing Scene data to an off-screen target. The data is read back after each call to draw + * into a local IntBuffer for use. + *

+ * + *

+ * Note: this class is not currently setup for use with other render contexts. + *

+ */ +public class LwjglHeadlessCanvas { + + protected Scene _scene; + protected Renderer _renderer = new LwjglRenderer(); + protected final DisplaySettings _settings; + protected Camera _camera; + + protected int _fboID, _depthRBID, _colorRBID; + protected IntBuffer _data; + protected Pbuffer _buff; + + /** + * Construct a new LwjglHeadlessCanvas. Only width, height, alpha, depth and stencil are used. Samples will be + * applied as well but may cause issues on some platforms. + * + * @param settings + * the settings to use. + * @param scene + * the scene we will render. + */ + public LwjglHeadlessCanvas(final DisplaySettings settings, final Scene scene) { + _scene = scene; + _settings = settings; + init(); + } + + protected void init() { + final int width = _settings.getWidth(); + final int height = _settings.getHeight(); + + try { + // Create a Pbuffer so we can have a valid gl context to work with + final PixelFormat format = new PixelFormat(_settings.getAlphaBits(), _settings.getDepthBits(), + _settings.getStencilBits()).withSamples(_settings.getSamples()); + _buff = new Pbuffer(1, 1, format, null); + _buff.makeCurrent(); + } catch (final LWJGLException ex) { + try { + // try again without samples + final PixelFormat format = new PixelFormat(_settings.getAlphaBits(), _settings.getDepthBits(), + _settings.getStencilBits()); + _buff = new Pbuffer(1, 1, format, null); + _buff.makeCurrent(); + } catch (final LWJGLException ex2) { + ex2.printStackTrace(); + } + } + + // Set up our Ardor3D context and capabilities objects + final LwjglContextCapabilities caps = new LwjglContextCapabilities(GLContext.getCapabilities()); + final RenderContext currentContext = new RenderContext(this, caps, null); + + if (!caps.isFBOSupported()) { + throw new Ardor3dException("Headless requires FBO support."); + } + + // Init our FBO. + final IntBuffer buffer = BufferUtils.createIntBuffer(1); + EXTFramebufferObject.glGenFramebuffersEXT(buffer); // generate id + + // Bind the FBO + _fboID = buffer.get(0); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, _fboID); + + // initial our color renderbuffer + EXTFramebufferObject.glGenRenderbuffersEXT(buffer); // generate id + _colorRBID = buffer.get(0); + EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, _colorRBID); + EXTFramebufferObject.glRenderbufferStorageEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, GL11.GL_RGBA, width, + height); + // Attach color renderbuffer to framebuffer + EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, _colorRBID); + + // initialize our depth renderbuffer + EXTFramebufferObject.glGenRenderbuffersEXT(buffer); // generate id + _depthRBID = buffer.get(0); + EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, _depthRBID); + EXTFramebufferObject.glRenderbufferStorageEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, + GL11.GL_DEPTH_COMPONENT, width, height); + // Attach depth renderbuffer to framebuffer + EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, _depthRBID); + + // Check FBO complete + LwjglTextureRenderer.checkFBOComplete(_fboID); + + // Setup our data buffer for storing rendered image data. + _data = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer(); + + // Add context to manager and set as active. + ContextManager.addContext(this, currentContext); + ContextManager.switchContext(this); + + // Turn on multisample if requested... + if (_settings.getSamples() != 0 && caps.isMultisampleSupported()) { + GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB); + } + + // Setup a default bg color. + _renderer.setBackgroundColor(ColorRGBA.BLACK); + + // Setup a default camera + _camera = new Camera(width, _settings.getHeight()); + _camera.setFrustumPerspective(45.0f, (float) width / (float) _settings.getHeight(), 1, 1000); + _camera.setProjectionMode(ProjectionMode.Perspective); + + // setup camera orientation and position. + final Vector3 loc = new Vector3(0.0f, 0.0f, 0.0f); + final Vector3 left = new Vector3(-1.0f, 0.0f, 0.0f); + final Vector3 up = new Vector3(0.0f, 1.0f, 0.0f); + final Vector3 dir = new Vector3(0.0f, 0f, -1.0f); + _camera.setFrame(loc, left, up, dir); + + if (Constants.useMultipleContexts) { + // release our FBO until used. + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0); + } + } + + public void draw() { + if (Constants.useMultipleContexts) { + // activate FBO + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, _fboID); + } + + // Make sure this OpenGL context is current. + ContextManager.switchContext(this); + try { + _buff.makeCurrent(); + } catch (final LWJGLException ex) { + ex.printStackTrace(); + } + + // make sure camera is set + if (Camera.getCurrentCamera() != _camera) { + _camera.update(); + } + _camera.apply(_renderer); + + // clear buffers + GL11.glDisable(GL11.GL_SCISSOR_TEST); + _renderer.clearBuffers(Renderer.BUFFER_COLOR | Renderer.BUFFER_DEPTH); + + // draw our scene + _scene.renderUnto(_renderer); + _renderer.flushFrame(false); + + // read data from our color buffer + _data.rewind(); + GL11.glReadBuffer(EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT); + GL11.glReadPixels(0, 0, _settings.getWidth(), _settings.getHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, _data); + + if (Constants.useMultipleContexts) { + // release our FBO. + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0); + } + } + + public void releaseContext() throws LWJGLException { + _buff.releaseContext(); + } + + public void cleanup() { + if (_fboID != 0) { + final IntBuffer id = BufferUtils.createIntBuffer(1); + id.put(_fboID); + id.rewind(); + EXTFramebufferObject.glDeleteFramebuffersEXT(id); + _fboID = 0; + } + + if (_depthRBID != 0) { + final IntBuffer id = BufferUtils.createIntBuffer(1); + id.put(_depthRBID); + id.rewind(); + EXTFramebufferObject.glDeleteRenderbuffersEXT(id); + _depthRBID = 0; + } + + if (_colorRBID != 0) { + final IntBuffer id = BufferUtils.createIntBuffer(1); + id.put(_colorRBID); + id.rewind(); + EXTFramebufferObject.glDeleteRenderbuffersEXT(id); + _colorRBID = 0; + } + ContextManager.removeContext(this); + } + + public IntBuffer getDataBuffer() { + return _data; + } + + public Renderer getRenderer() { + return _renderer; + } + + public Camera getCamera() { + return _camera; + } + + public void setCamera(final Camera camera) { + _camera = camera; + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglLibraryPaths.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglLibraryPaths.java new file mode 100644 index 0000000..b6e38df --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/framework/lwjgl/LwjglLibraryPaths.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + + +package com.ardor3d.framework.lwjgl; + +/** + * TODO: document this class! + * + */ +public enum LwjglLibraryPaths { + MACOSX("Mac OS X", null, new String[] { + "/macosx/libjinput-osx.jnilib", + "/macosx/liblwjgl.jnilib", + "/macosx/openal.dylib", + }), + LINUX_AMD64("Linux", "amd64", new String[] { + "/linux/libjinput-linux64.so", + "/linux/liblwjgl64.so", + "/linux/libopenal64.so", + }), + LINUX_I386("Linux", "i386", new String[] { + "/linux/libjinput-linux.so", + "/linux/liblwjgl.so", + "/linux/libopenal.so", + }), + // NOTE: the order of the elements in this array is significant, so the catchall has to come last, + // or it will shadow more specific alternatives + LINUX_CATCHALL("Linux", null, new String[] { + "/linux/libjinput-linux.so", + "/linux/liblwjgl.so", + "/linux/libopenal.so", + }), + SOLARIS("TODO", null, new String[] { + "/solaris/liblwjgl.so", + "/solaris/libopenal.so", + }), + WINDOWS_XP("Windows XP", null, new String[] { + "/windows/jinput-dx8.dll", + "/windows/jinput-raw.dll", + "/windows/lwjgl.dll", + "/windows/OpenAL32.dll", + }); + + private final String _operatingSystem; + private final String _architecture; + private final String[] _libraryPaths; + + + LwjglLibraryPaths(String operatingSystem, String architecture, String[] libraryPaths) { + _operatingSystem = operatingSystem; + _architecture = architecture; + _libraryPaths = libraryPaths; + } + + public static String[] getLibraryPaths(String operatingSystem, String architecture) { + for (LwjglLibraryPaths libraryPath : values()) { + if (operatingSystem.equals(libraryPath._operatingSystem) && + (libraryPath._architecture == null || architecture.equals(libraryPath._architecture))) { + return libraryPath._libraryPaths; + } + } + + throw new IllegalStateException("No matching set of library paths found for " + operatingSystem + ", " + architecture); + } + + public static void main(String[] args) { + System.out.println(System.getProperty("os.name")); + System.out.println(System.getProperty("os.arch")); + + System.getProperties(); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/input/jinput/JInputControllerWrapper.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/jinput/JInputControllerWrapper.java new file mode 100644 index 0000000..fed0775 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/jinput/JInputControllerWrapper.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.jinput; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.java.games.input.Component; +import net.java.games.input.Component.Identifier; +import net.java.games.input.Controller; +import net.java.games.input.Controller.Type; +import net.java.games.input.ControllerEnvironment; +import net.java.games.input.Event; + +import com.ardor3d.input.ControllerEvent; +import com.ardor3d.input.ControllerInfo; +import com.ardor3d.input.ControllerState; +import com.ardor3d.input.ControllerWrapper; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.Lists; +import com.google.common.collect.PeekingIterator; + +public class JInputControllerWrapper implements ControllerWrapper { + + protected final Event _event = new Event(); + protected final List _events = Collections.synchronizedList(new ArrayList()); + protected JInputControllerEventIterator _eventsIt = new JInputControllerEventIterator(); + protected final List _controllers = Lists.newArrayList(); + protected static boolean _inited = false; + + public PeekingIterator getEvents() { + init(); + if (!_eventsIt.hasNext()) { + _eventsIt = new JInputControllerEventIterator(); + } + for (final Controller controller : ControllerEnvironment.getDefaultEnvironment().getControllers()) { + controller.poll(); + while (controller.getEventQueue().getNextEvent(_event)) { + if (controller.getType() != Type.KEYBOARD && controller.getType() != Type.MOUSE) { + _events.add(createControllerEvent(controller, _event)); + } + } + } + + return _eventsIt; + } + + @Override + public int getControllerCount() { + init(); + return _controllers.size(); + } + + @Override + public ControllerInfo getControllerInfo(final int controllerIndex) { + init(); + return _controllers.get(controllerIndex); + } + + public synchronized void init() { + if (_inited) { + return; + } + + try { + ControllerEnvironment.getDefaultEnvironment(); + + for (final Controller controller : ControllerEnvironment.getDefaultEnvironment().getControllers()) { + if (controller.getType() != Type.KEYBOARD && controller.getType() != Type.MOUSE) { + _controllers.add(getControllerInfo(controller)); + for (final Component component : controller.getComponents()) { + ControllerState.NOTHING.set(controller.getName(), component.getIdentifier().getName(), 0); + } + } + } + } catch (final Exception e) { + e.printStackTrace(); + } finally { + _inited = true; + } + } + + protected ControllerInfo getControllerInfo(final Controller controller) { + final List axisNames = Lists.newArrayList(); + final List buttonNames = Lists.newArrayList(); + + for (final Component comp : controller.getComponents()) { + if (comp.getIdentifier() instanceof Identifier.Axis) { + axisNames.add(comp.getName()); + } else if (comp.getIdentifier() instanceof Identifier.Button) { + buttonNames.add(comp.getName()); + } + } + + return new ControllerInfo(controller.getName(), axisNames, buttonNames); + } + + protected ControllerEvent createControllerEvent(final Controller controller, final Event event) { + return new ControllerEvent(event.getNanos(), controller.getName(), event.getComponent().getIdentifier() + .getName(), event.getValue()); + } + + protected class JInputControllerEventIterator extends AbstractIterator implements + PeekingIterator { + + @Override + protected ControllerEvent computeNext() { + if (_events.size() > 0) { + final ControllerEvent controllerEvent = _events.remove(0); + return controllerEvent; + } else { + return endOfData(); + } + } + } +} \ No newline at end of file diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglControllerWrapper.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglControllerWrapper.java new file mode 100644 index 0000000..d92fdc3 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglControllerWrapper.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.lwjgl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.lwjgl.input.Controller; +import org.lwjgl.input.Controllers; + +import com.ardor3d.input.ControllerEvent; +import com.ardor3d.input.ControllerInfo; +import com.ardor3d.input.ControllerState; +import com.ardor3d.input.ControllerWrapper; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.Lists; +import com.google.common.collect.PeekingIterator; + +public class LwjglControllerWrapper implements ControllerWrapper { + + protected static boolean _inited = false; + protected final List _events = Collections.synchronizedList(new ArrayList()); + protected LwjglControllerEventIterator _eventsIt = new LwjglControllerEventIterator(); + protected final List _controllers = Lists.newArrayList(); + + private class LwjglControllerEventIterator extends AbstractIterator implements + PeekingIterator { + + @Override + protected ControllerEvent computeNext() { + if (_events.size() > 0) { + return _events.remove(0); + } else { + return endOfData(); + } + } + } + + public PeekingIterator getEvents() { + init(); + if (!_eventsIt.hasNext()) { + _eventsIt = new LwjglControllerEventIterator(); + } + + while (Controllers.next()) { + final Controller source = Controllers.getEventSource(); + if (Controllers.isEventButton()) { + _events.add(new ControllerEvent(Controllers.getEventNanoseconds(), source.getName(), source + .getButtonName(Controllers.getEventControlIndex()), source.isButtonPressed(Controllers + .getEventControlIndex()) ? 1f : 0f)); + } else if (Controllers.isEventAxis()) { + _events.add(new ControllerEvent(Controllers.getEventNanoseconds(), source.getName(), source + .getAxisName(Controllers.getEventControlIndex()), source.getAxisValue(Controllers + .getEventControlIndex()))); + } else if (Controllers.isEventPovX()) { + _events.add(new ControllerEvent(Controllers.getEventNanoseconds(), source.getName(), "Pov X", source + .getPovX())); + } else if (Controllers.isEventPovY()) { + _events.add(new ControllerEvent(Controllers.getEventNanoseconds(), source.getName(), "Pov Y", source + .getPovY())); + } + } + + return _eventsIt; + } + + public synchronized void init() { + if (_inited) { + return; + } + try { + Controllers.create(); + for (int i = 0, max = Controllers.getControllerCount(); i < max; i++) { + final Controller controller = Controllers.getController(i); + _controllers.add(getControllerInfo(controller)); + for (int j = 0; j < controller.getAxisCount(); j++) { + ControllerState.NOTHING.set(controller.getName(), controller.getAxisName(j), 0); + } + for (int j = 0; j < controller.getButtonCount(); j++) { + ControllerState.NOTHING.set(controller.getName(), controller.getButtonName(j), 0); + } + } + } catch (final Exception e) { + e.printStackTrace(); + } finally { + _inited = true; + } + } + + protected ControllerInfo getControllerInfo(final Controller controller) { + final List axisNames = Lists.newArrayList(); + final List buttonNames = Lists.newArrayList(); + + for (int i = 0; i < controller.getAxisCount(); i++) { + axisNames.add(controller.getAxisName(i)); + } + for (int i = 0; i < controller.getButtonCount(); i++) { + buttonNames.add(controller.getButtonName(i)); + } + + return new ControllerInfo(controller.getName(), axisNames, buttonNames); + } + + @Override + public int getControllerCount() { + init(); + return Controllers.getControllerCount(); + } + + @Override + public ControllerInfo getControllerInfo(final int controllerIndex) { + init(); + return _controllers.get(controllerIndex); + } +} \ No newline at end of file diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKey.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKey.java new file mode 100644 index 0000000..8b4a03b --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKey.java @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.lwjgl; + +import org.lwjgl.input.Keyboard; + +import com.ardor3d.input.Key; + +public enum LwjglKey { + + ZERO(Keyboard.KEY_0, Key.ZERO), // + ONE(Keyboard.KEY_1, Key.ONE), // + TWO(Keyboard.KEY_2, Key.TWO), // + THREE(Keyboard.KEY_3, Key.THREE), // + FOUR(Keyboard.KEY_4, Key.FOUR), // + FIVE(Keyboard.KEY_5, Key.FIVE), // + SIX(Keyboard.KEY_6, Key.SIX), // + SEVEN(Keyboard.KEY_7, Key.SEVEN), // + EIGHT(Keyboard.KEY_8, Key.EIGHT), // + NINE(Keyboard.KEY_9, Key.NINE), // + A(Keyboard.KEY_A, Key.A), // + ADD(Keyboard.KEY_ADD, Key.NUMPADADD), // + APOSTROPHE(Keyboard.KEY_APOSTROPHE, Key.APOSTROPHE), // + APPS(Keyboard.KEY_APPS, Key.APPS), // + AT(Keyboard.KEY_AT, Key.AT), // + AX(Keyboard.KEY_AX, Key.AX), // + B(Keyboard.KEY_B, Key.B), // + BACK(Keyboard.KEY_BACK, Key.BACK), // + BACKSLASH(Keyboard.KEY_BACKSLASH, Key.BACKSLASH), // + C(Keyboard.KEY_C, Key.C), // + CAPITAL(Keyboard.KEY_CAPITAL, Key.CAPITAL), // + CIRCUMFLEX(Keyboard.KEY_CIRCUMFLEX, Key.CIRCUMFLEX), // + COLON(Keyboard.KEY_COLON, Key.COLON), // + COMMA(Keyboard.KEY_COMMA, Key.COMMA), // + CONVERT(Keyboard.KEY_CONVERT, Key.CONVERT), // + D(Keyboard.KEY_D, Key.D), // + DECIMAL(Keyboard.KEY_DECIMAL, Key.DECIMAL), // + DELETE(Keyboard.KEY_DELETE, Key.DELETE), // + DIVIDE(Keyboard.KEY_DIVIDE, Key.DIVIDE), // + DOWN(Keyboard.KEY_DOWN, Key.DOWN), // + E(Keyboard.KEY_E, Key.E), // + END(Keyboard.KEY_END, Key.END), // + EQUALS(Keyboard.KEY_EQUALS, Key.EQUALS), // + ESCAPE(Keyboard.KEY_ESCAPE, Key.ESCAPE), // + F(Keyboard.KEY_F, Key.F), // + F1(Keyboard.KEY_F1, Key.F1), // + F2(Keyboard.KEY_F2, Key.F2), // + F3(Keyboard.KEY_F3, Key.F3), // + F4(Keyboard.KEY_F4, Key.F4), // + F5(Keyboard.KEY_F5, Key.F5), // + F6(Keyboard.KEY_F6, Key.F6), // + F7(Keyboard.KEY_F7, Key.F7), // + F8(Keyboard.KEY_F8, Key.F8), // + F9(Keyboard.KEY_F9, Key.F9), // + F10(Keyboard.KEY_F10, Key.F10), // + F11(Keyboard.KEY_F11, Key.F11), // + F12(Keyboard.KEY_F12, Key.F12), // + F13(Keyboard.KEY_F13, Key.F13), // + F14(Keyboard.KEY_F14, Key.F14), // + F15(Keyboard.KEY_F15, Key.F15), // + G(Keyboard.KEY_G, Key.G), // + GRAVE(Keyboard.KEY_GRAVE, Key.GRAVE), // + H(Keyboard.KEY_H, Key.H), // + HOME(Keyboard.KEY_HOME, Key.HOME), // + I(Keyboard.KEY_I, Key.I), // + INSERT(Keyboard.KEY_INSERT, Key.INSERT), // + J(Keyboard.KEY_J, Key.J), // + K(Keyboard.KEY_K, Key.K), // + KANA(Keyboard.KEY_KANA, Key.KANA), // + KANJI(Keyboard.KEY_KANJI, Key.KANJI), // + L(Keyboard.KEY_L, Key.L), // + LBRACKET(Keyboard.KEY_LBRACKET, Key.LBRACKET), // + LCONTROL(Keyboard.KEY_LCONTROL, Key.LCONTROL), // + LEFT(Keyboard.KEY_LEFT, Key.LEFT), // + LMENU(Keyboard.KEY_LMENU, Key.LMENU), // + LMETA(Keyboard.KEY_LMETA, Key.LMETA), // + LSHIFT(Keyboard.KEY_LSHIFT, Key.LSHIFT), // + M(Keyboard.KEY_M, Key.M), // + MINUS(Keyboard.KEY_MINUS, Key.MINUS), // + MULTIPLY(Keyboard.KEY_MULTIPLY, Key.MULTIPLY), // + N(Keyboard.KEY_N, Key.N), // + NEXT(Keyboard.KEY_NEXT, Key.PAGEDOWN_NEXT), // + NOCONVERT(Keyboard.KEY_NOCONVERT, Key.NOCONVERT), // + NUMLOCK(Keyboard.KEY_NUMLOCK, Key.NUMLOCK), // + NUMPAD0(Keyboard.KEY_NUMPAD0, Key.NUMPAD0), // + NUMPAD1(Keyboard.KEY_NUMPAD1, Key.NUMPAD1), // + NUMPAD2(Keyboard.KEY_NUMPAD2, Key.NUMPAD2), // + NUMPAD3(Keyboard.KEY_NUMPAD3, Key.NUMPAD3), // + NUMPAD4(Keyboard.KEY_NUMPAD4, Key.NUMPAD4), // + NUMPAD5(Keyboard.KEY_NUMPAD5, Key.NUMPAD5), // + NUMPAD6(Keyboard.KEY_NUMPAD6, Key.NUMPAD6), // + NUMPAD7(Keyboard.KEY_NUMPAD7, Key.NUMPAD7), // + NUMPAD8(Keyboard.KEY_NUMPAD8, Key.NUMPAD8), // + NUMPAD9(Keyboard.KEY_NUMPAD9, Key.NUMPAD9), // + NUMPADCOMMA(Keyboard.KEY_NUMPADCOMMA, Key.NUMPADCOMMA), // + NUMPADENTER(Keyboard.KEY_NUMPADENTER, Key.NUMPADENTER), // + NUMPADEQUALS(Keyboard.KEY_NUMPADEQUALS, Key.NUMPADEQUALS), // + O(Keyboard.KEY_O, Key.O), // + P(Keyboard.KEY_P, Key.P), // + PAUSE(Keyboard.KEY_PAUSE, Key.PAUSE), // + POWER(Keyboard.KEY_POWER, Key.POWER), // + PERIOD(Keyboard.KEY_PERIOD, Key.PERIOD), // + PRIOR(Keyboard.KEY_PRIOR, Key.PAGEUP_PRIOR), // + Q(Keyboard.KEY_Q, Key.Q), // + R(Keyboard.KEY_R, Key.R), // + RBRACKET(Keyboard.KEY_RBRACKET, Key.RBRACKET), // + RCONTROL(Keyboard.KEY_RCONTROL, Key.RCONTROL), // + RETURN(Keyboard.KEY_RETURN, Key.RETURN), // + RIGHT(Keyboard.KEY_RIGHT, Key.RIGHT), // + RMENU(Keyboard.KEY_RMENU, Key.RMENU), // + RMETA(Keyboard.KEY_RMETA, Key.RMETA), // + RSHIFT(Keyboard.KEY_RSHIFT, Key.RSHIFT), // + S(Keyboard.KEY_S, Key.S), // + SCROLL(Keyboard.KEY_SCROLL, Key.SCROLL), // + SEMICOLON(Keyboard.KEY_SEMICOLON, Key.SEMICOLON), // + SLASH(Keyboard.KEY_SLASH, Key.SLASH), // + SLEEP(Keyboard.KEY_SLEEP, Key.SLEEP), // + SPACE(Keyboard.KEY_SPACE, Key.SPACE), // + STOP(Keyboard.KEY_STOP, Key.STOP), // + NUMPADSUBTRACT(Keyboard.KEY_SUBTRACT, Key.NUMPADSUBTRACT), // XXX: Not sure if this is correct? + SYSRQ(Keyboard.KEY_SYSRQ, Key.SYSRQ), // + T(Keyboard.KEY_T, Key.T), // + TAB(Keyboard.KEY_TAB, Key.TAB), // + U(Keyboard.KEY_U, Key.U), // + UNDERLINE(Keyboard.KEY_UNDERLINE, Key.UNDERLINE), // + UNLABELED(Keyboard.KEY_UNLABELED, Key.UNLABELED), // + UP(Keyboard.KEY_UP, Key.UP), // + V(Keyboard.KEY_V, Key.V), // + W(Keyboard.KEY_W, Key.W), // + X(Keyboard.KEY_X, Key.X), // + Y(Keyboard.KEY_Y, Key.Y), // + YEN(Keyboard.KEY_YEN, Key.YEN), // + Z(Keyboard.KEY_Z, Key.Z), // + NONE(Keyboard.KEY_NONE, Key.UNKNOWN); + + private final int _lwjglCode; + private final Key _key; + + private LwjglKey(final int lwjglCode, final Key key) { + _lwjglCode = lwjglCode; + _key = key; + } + + /** + * Locate a key, given a specific lwjgl key code. + * + * @param lwjglCode + * the lwjgl key code. + * @return the Ardor3D Key enum value. + */ + public static Key findByCode(final int lwjglCode) { + for (final LwjglKey ak : values()) { + if (ak._lwjglCode == lwjglCode) { + return ak._key; + } + } + + return Key.UNKNOWN; + } + + public int getLwjglCode() { + return _lwjglCode; + } + +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKeyboardWrapper.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKeyboardWrapper.java new file mode 100644 index 0000000..de4c991 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglKeyboardWrapper.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.lwjgl; + +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Keyboard; + +import com.ardor3d.input.Key; +import com.ardor3d.input.KeyEvent; +import com.ardor3d.input.KeyState; +import com.ardor3d.input.KeyboardWrapper; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.PeekingIterator; + +/** + * Wraps the {@link org.lwjgl.input.Keyboard} class. + */ +public class LwjglKeyboardWrapper implements KeyboardWrapper { + private LwjglKeyEventIterator _currentIterator = null; + + public void init() { + if (!Keyboard.isCreated()) { + try { + Keyboard.create(); + } catch (final LWJGLException e) { + throw new RuntimeException(e); + } + } + } + + public PeekingIterator getEvents() { + if (_currentIterator == null || !_currentIterator.hasNext()) { + _currentIterator = new LwjglKeyEventIterator(); + } + + return _currentIterator; + } + + private static class LwjglKeyEventIterator extends AbstractIterator implements PeekingIterator { + + @Override + protected KeyEvent computeNext() { + if (!Keyboard.next()) { + return endOfData(); + } + + final int keyCode = Keyboard.getEventKey(); + final boolean pressed = Keyboard.getEventKeyState(); + final char keyChar = Keyboard.getEventCharacter(); + + final Key k = LwjglKey.findByCode(keyCode); + + return new KeyEvent(k, pressed ? KeyState.DOWN : KeyState.UP, keyChar); + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseManager.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseManager.java new file mode 100644 index 0000000..b689841 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseManager.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Cursor; +import org.lwjgl.input.Mouse; + +import com.ardor3d.annotation.MainThread; +import com.ardor3d.image.Image; +import com.ardor3d.image.ImageDataFormat; +import com.ardor3d.input.GrabbedState; +import com.ardor3d.input.MouseCursor; +import com.ardor3d.input.MouseManager; +import com.ardor3d.util.geom.BufferUtils; + +/** + * LWJGL-specific implementation of the {@link com.ardor3d.input.MouseManager} interface. No methods in this class + * should be called before the LWJGL Display has been initialized, since this class is dependent on being able to + * initialize the {@link org.lwjgl.input.Mouse} class. + */ +public class LwjglMouseManager implements MouseManager { + private boolean _inited = false; + + private void init() { + if (!_inited) { + if (!Mouse.isCreated()) { + try { + Mouse.create(); + } catch (final Exception e) { + // this typically happens if the Display hasn't been initialized. + throw new RuntimeException("Unable to initialise mouse manager", e); + } + } + _inited = true; + } + } + + @MainThread + public void setCursor(final MouseCursor cursor) { + init(); + + try { + final Cursor lwjglCursor = createLwjglCursor(cursor); + + if (lwjglCursor == null || !lwjglCursor.equals(Mouse.getNativeCursor())) { + Mouse.setNativeCursor(lwjglCursor); + } + } catch (final LWJGLException e) { + throw new RuntimeException("Unable to set cursor", e); + } + } + + private Cursor createLwjglCursor(final MouseCursor cursor) throws LWJGLException { + if (cursor == MouseCursor.SYSTEM_DEFAULT) { + return null; // setting the cursor to null in LWJGL means using the system default one + } + + final boolean eightBitAlpha = (Cursor.getCapabilities() & Cursor.CURSOR_8_BIT_ALPHA) != 0; + + final Image image = cursor.getImage(); + + final boolean isRgba = image.getDataFormat() == ImageDataFormat.RGBA; + final int imageWidth = image.getWidth(); + final int imageHeight = image.getHeight(); + + final ByteBuffer imageData = image.getData(0); + imageData.rewind(); + final IntBuffer imageDataCopy = BufferUtils.createIntBuffer(imageWidth * imageHeight); + + for (int y = 0; y < imageHeight; y++) { + for (int x = 0; x < imageWidth; x++) { + final int index = y * imageWidth + x; + + int r = imageData.get() & 0xff; + int g = imageData.get() & 0xff; + int b = imageData.get() & 0xff; + int a = 0xff; + if (isRgba) { + a = imageData.get() & 0xff; + if (!eightBitAlpha) { + if (a < 0x7f) { + a = 0x00; + // small hack to prevent triggering "reverse screen" on windows. + r = g = b = 0; + } else { + a = 0xff; + } + } + } + + imageDataCopy.put(index, (a << 24) | (r << 16) | (g << 8) | b); + } + } + + return new Cursor(imageWidth, imageHeight, cursor.getHotspotX(), cursor.getHotspotY(), 1, imageDataCopy, null); + } + + public void setPosition(final int x, final int y) { + init(); + + Mouse.setCursorPosition(x, y); + } + + public void setGrabbed(final GrabbedState grabbedState) { + init(); + + switch (grabbedState) { + case GRABBED: + Mouse.setGrabbed(true); + break; + case NOT_GRABBED: + Mouse.setGrabbed(false); + break; + default: + throw new IllegalStateException("Unhandled GrabbedState: " + grabbedState); + } + } + + public boolean isSetPositionSupported() { + return true; + } + + public boolean isSetGrabbedSupported() { + return true; + } + + public GrabbedState getGrabbed() { + return Mouse.isGrabbed() ? GrabbedState.GRABBED : GrabbedState.NOT_GRABBED; + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseWrapper.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseWrapper.java new file mode 100644 index 0000000..d554183 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseWrapper.java @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.input.lwjgl; + +import java.util.EnumMap; +import java.util.EnumSet; + +import org.lwjgl.input.Mouse; + +import com.ardor3d.input.ButtonState; +import com.ardor3d.input.MouseButton; +import com.ardor3d.input.MouseState; +import com.ardor3d.input.MouseWrapper; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.EnumMultiset; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.PeekingIterator; + +/** + * Wrapper over the {@link org.lwjgl.input.Mouse} mouse interface class. + */ +public class LwjglMouseWrapper implements MouseWrapper { + private LwjglMouseIterator _currentIterator = null; + + private static final Multiset _clicks = EnumMultiset.create(MouseButton.class); + private static final EnumMap _lastClickTime = Maps.newEnumMap(MouseButton.class); + private static final EnumSet _clickArmed = EnumSet.noneOf(MouseButton.class); + + private static boolean _sendClickState = false; + private static MouseState _nextState; + + public LwjglMouseWrapper() { + for (final MouseButton mb : MouseButton.values()) { + _lastClickTime.put(mb, 0L); + } + } + + public void init() { + if (!Mouse.isCreated()) { + try { + Mouse.create(); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + } + + public PeekingIterator getEvents() { + // only create a new iterator if there isn't an existing, valid, one. + if (_currentIterator == null || !_currentIterator.hasNext()) { + _currentIterator = new LwjglMouseIterator(); + } + + return _currentIterator; + } + + private static class LwjglMouseIterator extends AbstractIterator implements PeekingIterator { + + public LwjglMouseIterator() {} + + @Override + protected MouseState computeNext() { + if (_nextState != null) { + final MouseState rVal = _nextState; + _nextState = null; + return rVal; + } + + if (!Mouse.next()) { + return endOfData(); + } + + final EnumMap buttons = Maps.newEnumMap(MouseButton.class); + + if (Mouse.getButtonCount() > 0) { + final boolean down = Mouse.isButtonDown(0); + processButtonForClick(MouseButton.LEFT, down); + buttons.put(MouseButton.LEFT, down ? ButtonState.DOWN : ButtonState.UP); + } + if (Mouse.getButtonCount() > 1) { + final boolean down = Mouse.isButtonDown(1); + processButtonForClick(MouseButton.RIGHT, down); + buttons.put(MouseButton.RIGHT, down ? ButtonState.DOWN : ButtonState.UP); + } + if (Mouse.getButtonCount() > 2) { + final boolean down = Mouse.isButtonDown(2); + processButtonForClick(MouseButton.MIDDLE, down); + buttons.put(MouseButton.MIDDLE, down ? ButtonState.DOWN : ButtonState.UP); + } + + final MouseState nextState = new MouseState(Mouse.getEventX(), Mouse.getEventY(), Mouse.getEventDX(), Mouse + .getEventDY(), Mouse.getEventDWheel(), buttons, null); + + if (nextState.getDx() != 0.0 || nextState.getDy() != 0.0) { + _clickArmed.clear(); + _clicks.clear(); + _sendClickState = false; + } + + if (_sendClickState) { + _nextState = nextState; + _sendClickState = false; + return new MouseState(nextState.getX(), nextState.getY(), nextState.getDx(), nextState.getDy(), + nextState.getDwheel(), buttons, EnumMultiset.create(_clicks)); + } else { + return nextState; + } + } + + private void processButtonForClick(final MouseButton b, final boolean down) { + boolean expired = false; + if (System.currentTimeMillis() - _lastClickTime.get(b) > MouseState.CLICK_TIME_MS) { + _clicks.setCount(b, 0); + expired = true; + } + if (down) { + if (_clickArmed.contains(b)) { + _clicks.setCount(b, 0); + } + _clickArmed.add(b); + _lastClickTime.put(b, System.currentTimeMillis()); + } else { + if (!expired && _clickArmed.contains(b)) { + _clicks.add(b); // increment count of clicks for button b. + // XXX: Note the double event add... this prevents sticky click counts, but is it the best way? + _sendClickState = true; + } else { + _clicks.setCount(b, 0); // clear click count for button b. + } + _clickArmed.remove(b); + } + } + + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglContextCapabilities.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglContextCapabilities.java new file mode 100644 index 0000000..af52d28 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglContextCapabilities.java @@ -0,0 +1,237 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.renderer.lwjgl; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.opengl.ARBFragmentShader; +import org.lwjgl.opengl.ARBMultitexture; +import org.lwjgl.opengl.ARBVertexShader; +import org.lwjgl.opengl.EXTFramebufferMultisample; +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.EXTTextureFilterAnisotropic; +import org.lwjgl.opengl.EXTTextureLODBias; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20; + +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.util.geom.BufferUtils; + +public class LwjglContextCapabilities extends ContextCapabilities { + + public LwjglContextCapabilities(final ContextCapabilities caps) { + super(caps); + } + + public LwjglContextCapabilities(final org.lwjgl.opengl.ContextCapabilities caps) { + final IntBuffer buf = BufferUtils.createIntBuffer(16); + + _supportsVBO = caps.GL_ARB_vertex_buffer_object; + _supportsGL1_2 = caps.OpenGL12; + _supportsMultisample = caps.GL_ARB_multisample; + + _supportsConstantColor = _supportsEq = caps.GL_ARB_imaging; + _supportsSeparateFunc = caps.GL_EXT_blend_func_separate; + _supportsSeparateEq = caps.GL_EXT_blend_equation_separate; + _supportsMinMax = caps.GL_EXT_blend_minmax; + _supportsSubtract = caps.GL_EXT_blend_subtract; + + _supportsFogCoords = caps.GL_EXT_fog_coord; + _supportsFragmentProgram = caps.GL_ARB_fragment_program; + _supportsVertexProgram = caps.GL_ARB_vertex_program; + + _supportsPointSprites = caps.GL_ARB_point_sprite; + _supportsPointParameters = caps.GL_ARB_point_parameters; + + _supportsTextureLodBias = caps.GL_EXT_texture_lod_bias; + if (_supportsTextureLodBias) { + GL11.glGetInteger(EXTTextureLODBias.GL_MAX_TEXTURE_LOD_BIAS_EXT, buf); + _maxTextureLodBias = buf.get(0); + } else { + _maxTextureLodBias = 0f; + } + + GL11.glGetInteger(GL11.GL_MAX_CLIP_PLANES, buf); + _maxUserClipPlanes = buf.get(0); + + _glslSupported = caps.GL_ARB_shader_objects && caps.GL_ARB_fragment_shader && caps.GL_ARB_vertex_shader + && caps.GL_ARB_shading_language_100; + + _geometryShader4Supported = caps.GL_ARB_geometry_shader4 && _glslSupported; + + _geometryInstancingSupported = caps.GL_EXT_draw_instanced || caps.OpenGL30; + + if (_glslSupported) { + GL11.glGetInteger(ARBVertexShader.GL_MAX_VERTEX_ATTRIBS_ARB, buf); + _maxGLSLVertexAttribs = buf.get(0); + } + + // Pbuffer + _pbufferSupported = caps.GL_ARB_pixel_buffer_object; + + // FBO + _fboSupported = caps.GL_EXT_framebuffer_object; + if (_fboSupported) { + if (caps.GL_ARB_draw_buffers) { + GL11.glGetInteger(EXTFramebufferObject.GL_MAX_COLOR_ATTACHMENTS_EXT, buf); + _maxFBOColorAttachments = buf.get(0); + } else { + _maxFBOColorAttachments = 1; + } + + // Max multisample samples. + if (caps.GL_EXT_framebuffer_multisample && caps.GL_EXT_framebuffer_blit) { + GL11.glGetInteger(EXTFramebufferMultisample.GL_MAX_SAMPLES_EXT, buf); + _maxFBOSamples = buf.get(0); + } else { + _maxFBOSamples = 0; + } + } else { + _maxFBOColorAttachments = 0; + } + + _twoSidedStencilSupport = caps.GL_EXT_stencil_two_side; + _stencilWrapSupport = caps.GL_EXT_stencil_wrap; + + // number of available auxiliary draw buffers + GL11.glGetInteger(GL11.GL_AUX_BUFFERS, buf); + _numAuxDrawBuffers = buf.get(0); + + // max texture size. + GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE, buf); + _maxTextureSize = buf.get(0); + + // Check for support of multitextures. + _supportsMultiTexture = caps.GL_ARB_multitexture; + + // Support for texture formats + _supportsFloatTextures = caps.GL_ARB_texture_float; + _supportsIntegerTextures = caps.GL_EXT_texture_integer; + _supportsOneTwoComponentTextures = caps.GL_ARB_texture_rg; + + // Check for support of fixed function dot3 environment settings + _supportsEnvDot3 = caps.GL_ARB_texture_env_dot3; + + // Check for support of fixed function dot3 environment settings + _supportsEnvCombine = caps.GL_ARB_texture_env_combine; + + // Check for support of automatic mipmap generation + _automaticMipMaps = caps.GL_SGIS_generate_mipmap; + + _supportsDepthTexture = caps.GL_ARB_depth_texture; + _supportsShadow = caps.GL_ARB_shadow; + + // If we do support multitexturing, find out how many textures we + // can handle. + if (_supportsMultiTexture) { + GL11.glGetInteger(ARBMultitexture.GL_MAX_TEXTURE_UNITS_ARB, buf); + _numFixedTexUnits = buf.get(0); + } else { + _numFixedTexUnits = 1; + } + + // Go on to check number of texture units supported for vertex and + // fragment shaders + if (caps.GL_ARB_shader_objects && caps.GL_ARB_vertex_shader && caps.GL_ARB_fragment_shader) { + GL11.glGetInteger(ARBVertexShader.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, buf); + _numVertexTexUnits = buf.get(0); + GL11.glGetInteger(ARBFragmentShader.GL_MAX_TEXTURE_IMAGE_UNITS_ARB, buf); + _numFragmentTexUnits = buf.get(0); + GL11.glGetInteger(ARBFragmentShader.GL_MAX_TEXTURE_COORDS_ARB, buf); + _numFragmentTexCoordUnits = buf.get(0); + } else { + // based on nvidia dev doc: + // http://developer.nvidia.com/object/General_FAQ.html#t6 + // "For GPUs that do not support GL_ARB_fragment_program and + // GL_NV_fragment_program, those two limits are set equal to + // GL_MAX_TEXTURE_UNITS." + _numFragmentTexCoordUnits = _numFixedTexUnits; + _numFragmentTexUnits = _numFixedTexUnits; + + // We'll set this to 0 for now since we do not know: + _numVertexTexUnits = 0; + } + // ARBShaderObjects.GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB + // caps.GL_EXT_bindable_uniform + // EXTBindableUniform.GL_MAX_BINDABLE_UNIFORM_SIZE_EXT; + + // Now determine the maximum number of supported texture units + _numTotalTexUnits = Math.max(_numFragmentTexCoordUnits, + Math.max(_numFixedTexUnits, Math.max(_numFragmentTexUnits, _numVertexTexUnits))); + + // Check for S3 texture compression capability. + _supportsS3TCCompression = caps.GL_EXT_texture_compression_s3tc; + + // Check for LA texture compression capability. + _supportsLATCCompression = caps.GL_EXT_texture_compression_latc; + + // Check for generic texture compression capability. + _supportsGenericCompression = caps.GL_ARB_texture_compression; + + // Check for 3D texture capability. + _supportsTexture3D = caps.OpenGL12; + + // Check for cubemap capability. + _supportsTextureCubeMap = caps.GL_ARB_texture_cube_map; + + // See if we support anisotropic filtering + _supportsAniso = caps.GL_EXT_texture_filter_anisotropic; + + if (_supportsAniso) { + // Due to LWJGL buffer check, you can't use smaller sized + // buffers (min_size = 16 for glGetFloat()). + final FloatBuffer max_a = BufferUtils.createFloatBuffer(16); + max_a.rewind(); + + // Grab the maximum anisotropic filter. + GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max_a); + + // set max. + _maxAnisotropic = max_a.get(0); + } + + // See if we support textures that are not power of 2 in size. + _supportsNonPowerTwo = caps.GL_ARB_texture_non_power_of_two; + + // See if we support textures that do not have width == height. + _supportsRectangular = caps.GL_ARB_texture_rectangle; + + _supportsMirroredRepeat = caps.GL_ARB_texture_mirrored_repeat; + _supportsMirrorClamp = _supportsMirrorEdgeClamp = _supportsMirrorBorderClamp = caps.GL_EXT_texture_mirror_clamp; + _supportsBorderClamp = caps.GL_ARB_texture_border_clamp; + _supportsEdgeClamp = _supportsGL1_2; + + try { + _displayVendor = GL11.glGetString(GL11.GL_VENDOR); + } catch (final Exception e) { + _displayVendor = "Unable to retrieve vendor."; + } + + try { + _displayRenderer = GL11.glGetString(GL11.GL_RENDERER); + } catch (final Exception e) { + _displayRenderer = "Unable to retrieve adapter details."; + } + + try { + _displayVersion = GL11.glGetString(GL11.GL_VERSION); + } catch (final Exception e) { + _displayVersion = "Unable to retrieve API version."; + } + + try { + _shadingLanguageVersion = GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION); + } catch (final Exception e) { + _shadingLanguageVersion = "Unable to retrieve shading language version."; + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglPbufferTextureRenderer.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglPbufferTextureRenderer.java new file mode 100644 index 0000000..da28b53 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglPbufferTextureRenderer.java @@ -0,0 +1,397 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.renderer.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.AWTGLCanvas; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GLContext; +import org.lwjgl.opengl.Pbuffer; +import org.lwjgl.opengl.PixelFormat; +import org.lwjgl.opengl.RenderTexture; + +import com.ardor3d.framework.DisplaySettings; +import com.ardor3d.framework.Scene; +import com.ardor3d.image.Texture; +import com.ardor3d.image.Texture.Type; +import com.ardor3d.math.MathUtils; +import com.ardor3d.renderer.AbstractPbufferTextureRenderer; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.TextureRendererFactory; +import com.ardor3d.renderer.state.RenderState; +import com.ardor3d.renderer.state.record.TextureRecord; +import com.ardor3d.renderer.state.record.TextureStateRecord; +import com.ardor3d.scene.state.lwjgl.LwjglTextureStateUtil; +import com.ardor3d.scene.state.lwjgl.util.LwjglTextureUtil; +import com.ardor3d.scenegraph.Spatial; +import com.ardor3d.util.Ardor3dException; +import com.ardor3d.util.TextureKey; +import com.ardor3d.util.geom.BufferUtils; + +/** + *

+ * This class is used by Ardor3D's LWJGL implementation to render textures. Users should not create this class + * directly. + *

+ * + * @see TextureRendererFactory + */ +public class LwjglPbufferTextureRenderer extends AbstractPbufferTextureRenderer { + private static final Logger logger = Logger.getLogger(LwjglPbufferTextureRenderer.class.getName()); + + /* Pbuffer instance */ + private Pbuffer _pbuffer; + + private RenderTexture _texture; + + public LwjglPbufferTextureRenderer(final DisplaySettings settings, final Renderer parentRenderer, + final ContextCapabilities caps) { + super(settings, parentRenderer, caps); + + int pTarget = RenderTexture.RENDER_TEXTURE_2D; + + if (!MathUtils.isPowerOfTwo(_width) || !MathUtils.isPowerOfTwo(_height)) { + pTarget = RenderTexture.RENDER_TEXTURE_RECTANGLE; + } + + // signature: boolean useRGB, boolean useRGBA, boolean useDepth, boolean isRectangle, int target, int mipmaps + _texture = new RenderTexture(false, true, true, pTarget == RenderTexture.RENDER_TEXTURE_RECTANGLE, pTarget, 0); + + setMultipleTargets(false); + } + + /** + * setupTexture initializes a new Texture object for use with TextureRenderer. Generates a valid gl + * texture id for this texture and inits the data type for the texture. + */ + public void setupTexture(final Texture tex) { + if (tex.getType() != Type.TwoDimensional) { + throw new IllegalArgumentException("Unsupported type: " + tex.getType()); + } + final RenderContext context = ContextManager.getCurrentContext(); + final TextureStateRecord record = (TextureStateRecord) context.getStateRecord(RenderState.StateType.Texture); + + // check if we are already setup... if so, throw error. + if (tex.getTextureKey() == null) { + tex.setTextureKey(TextureKey.getRTTKey(tex.getMinificationFilter())); + } else if (tex.getTextureIdForContext(context.getGlContextRep()) != 0) { + throw new Ardor3dException("Texture is already setup and has id."); + } + + // Create the texture + final IntBuffer ibuf = BufferUtils.createIntBuffer(1); + GL11.glGenTextures(ibuf); + final int textureId = ibuf.get(0); + tex.setTextureIdForContext(context.getGlContextRep(), textureId); + + LwjglTextureStateUtil.doTextureBind(tex, 0, true); + + // Initialize our texture with some default data. + final int internalFormat = LwjglTextureUtil.getGLInternalFormat(tex.getTextureStoreFormat()); + final int dataFormat = LwjglTextureUtil.getGLPixelFormatFromStoreFormat(tex.getTextureStoreFormat()); + final int pixelDataType = LwjglTextureUtil.getGLPixelDataType(tex.getRenderedTexturePixelDataType()); + + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, _width, _height, 0, dataFormat, pixelDataType, + (ByteBuffer) null); + + // Setup filtering and wrap + final TextureRecord texRecord = record.getTextureRecord(textureId, tex.getType()); + LwjglTextureStateUtil.applyFilter(tex, texRecord, 0, record, context.getCapabilities()); + LwjglTextureStateUtil.applyWrap(tex, texRecord, 0, record, context.getCapabilities()); + + logger.fine("setup pbuffer tex" + textureId + ": " + _width + "," + _height); + } + + public void render(final Spatial spat, final Texture tex, final int clear) { + render(null, spat, null, tex, clear); + } + + public void render(final List spat, final Texture tex, final int clear) { + render(spat, null, null, tex, clear); + } + + public void render(final Scene scene, final Texture tex, final int clear) { + render(null, null, scene, tex, clear); + } + + private void render(final List toDrawA, final Spatial toDrawB, final Scene toDrawC, + final Texture tex, final int clear) { + try { + if (_pbuffer == null || _pbuffer.isBufferLost()) { + if (_pbuffer != null && _pbuffer.isBufferLost()) { + logger.warning("PBuffer contents lost - will recreate the buffer"); + deactivate(); + _pbuffer.destroy(); + } + initPbuffer(); + } + + if (_useDirectRender && !tex.getTextureStoreFormat().isDepthFormat()) { + // setup and render directly to a 2d texture. + _pbuffer.releaseTexImage(Pbuffer.FRONT_LEFT_BUFFER); + activate(); + switchCameraIn(clear); + + if (toDrawA != null) { + doDraw(toDrawA); + } else if (toDrawB != null) { + doDraw(toDrawB); + } else { + doDraw(toDrawC); + } + + deactivate(); + switchCameraOut(); + LwjglTextureStateUtil.doTextureBind(tex, 0, true); + _pbuffer.bindTexImage(Pbuffer.FRONT_LEFT_BUFFER); + } else { + // render and copy to a texture + activate(); + switchCameraIn(clear); + + if (toDrawA != null) { + doDraw(toDrawA); + } else { + doDraw(toDrawB); + } + + switchCameraOut(); + + copyToTexture(tex, 0, 0, _width, _height, 0, 0); + + deactivate(); + } + + } catch (final Exception e) { + logger.logp(Level.SEVERE, this.getClass().toString(), "render(Spatial, Texture)", "Exception", e); + } + } + + public void render(final Spatial spat, final List texs, final int clear) { + render(null, spat, null, texs, clear); + } + + public void render(final List spat, final List texs, final int clear) { + render(spat, null, null, texs, clear); + } + + public void render(final Scene scene, final List texs, final int clear) { + render(null, null, scene, texs, clear); + } + + private void render(final List toDrawA, final Spatial toDrawB, final Scene toDrawC, + final List texs, final int clear) { + try { + if (_pbuffer == null || _pbuffer.isBufferLost()) { + if (_pbuffer != null && _pbuffer.isBufferLost()) { + logger.warning("PBuffer contents lost - will recreate the buffer"); + deactivate(); + _pbuffer.destroy(); + } + initPbuffer(); + } + + if (texs.size() == 1 && _useDirectRender && !texs.get(0).getTextureStoreFormat().isDepthFormat()) { + // setup and render directly to a 2d texture. + LwjglTextureStateUtil.doTextureBind(texs.get(0), 0, true); + activate(); + switchCameraIn(clear); + _pbuffer.releaseTexImage(Pbuffer.FRONT_LEFT_BUFFER); + + if (toDrawA != null) { + doDraw(toDrawA); + } else { + doDraw(toDrawB); + } + + switchCameraOut(); + + deactivate(); + _pbuffer.bindTexImage(Pbuffer.FRONT_LEFT_BUFFER); + } else { + // render and copy to a texture + activate(); + switchCameraIn(clear); + + if (toDrawA != null) { + doDraw(toDrawA); + } else { + doDraw(toDrawB); + } + + switchCameraOut(); + + for (int i = 0; i < texs.size(); i++) { + copyToTexture(texs.get(i), 0, 0, _width, _height, 0, 0); + } + + deactivate(); + } + + } catch (final Exception e) { + logger.logp(Level.SEVERE, this.getClass().toString(), "render(Spatial, Texture)", "Exception", e); + } + } + + public void copyToTexture(final Texture tex, final int x, final int y, final int width, final int height, + final int xoffset, final int yoffset) { + LwjglTextureStateUtil.doTextureBind(tex, 0, true); + + GL11.glCopyTexSubImage2D(GL11.GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, width, height); + } + + @Override + protected void clearBuffers(final int clear) { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + _parentRenderer.clearBuffers(clear); + } + + private void initPbuffer() { + + try { + if (_pbuffer != null) { + giveBackContext(); + ContextManager.removeContext(_pbuffer); + } + final PixelFormat format = new PixelFormat(_settings.getAlphaBits(), _settings.getDepthBits(), + _settings.getStencilBits()).withSamples(_settings.getSamples()) + .withBitsPerPixel(_settings.getColorDepth()).withStereo(_settings.isStereo()); + _pbuffer = new Pbuffer(_width, _height, format, _texture, null); + final Object contextKey = _pbuffer; + try { + _pbuffer.makeCurrent(); + } catch (final LWJGLException e) { + throw new RuntimeException(e); + } + + final LwjglContextCapabilities caps = new LwjglContextCapabilities(GLContext.getCapabilities()); + ContextManager.addContext(contextKey, + new RenderContext(contextKey, caps, ContextManager.getCurrentContext())); + + } catch (final Exception e) { + logger.logp(Level.SEVERE, this.getClass().toString(), "initPbuffer()", "Exception", e); + + if (_texture != null && _useDirectRender) { + logger.warning("Your card claims to support Render to Texture but fails to enact it. Updating your driver might solve this problem."); + logger.warning("Attempting to fall back to Copy Texture."); + _texture = null; + _useDirectRender = false; + initPbuffer(); + return; + } + + logger.log(Level.WARNING, "Failed to create Pbuffer.", e); + return; + } + + try { + activate(); + + _width = _pbuffer.getWidth(); + _height = _pbuffer.getHeight(); + + deactivate(); + } catch (final Exception e) { + logger.log(Level.WARNING, "Failed to initialize created Pbuffer.", e); + return; + } + } + + private void activate() { + if (_active == 0) { + try { + _oldContext = ContextManager.getCurrentContext(); + _pbuffer.makeCurrent(); + + ContextManager.switchContext(_pbuffer); + + ContextManager.getCurrentContext().clearEnforcedStates(); + ContextManager.getCurrentContext().enforceStates(_enforcedStates); + + if (_bgColorDirty) { + GL11.glClearColor(_backgroundColor.getRed(), _backgroundColor.getGreen(), + _backgroundColor.getBlue(), _backgroundColor.getAlpha()); + _bgColorDirty = false; + } + } catch (final LWJGLException e) { + logger.logp(Level.SEVERE, this.getClass().toString(), "activate()", "Exception", e); + throw new Ardor3dException(); + } + } + _active++; + } + + private void deactivate() { + if (_active == 1) { + try { + giveBackContext(); + } catch (final LWJGLException e) { + logger.logp(Level.SEVERE, this.getClass().toString(), "deactivate()", "Exception", e); + throw new Ardor3dException(); + } + } + _active--; + } + + // XXX: Need another look at this to make it generic? + private void giveBackContext() throws LWJGLException { + if (Display.isCreated()) { + Display.makeCurrent(); + ContextManager.switchContext(_oldContext.getContextKey()); + } else if (_oldContext.getContextKey() instanceof AWTGLCanvas) { + ((AWTGLCanvas) _oldContext.getContextKey()).makeCurrent(); + ContextManager.switchContext(_oldContext.getContextKey()); + } + } + + public void cleanup() { + ContextManager.removeContext(_pbuffer); + _pbuffer.destroy(); + } + + public void setMultipleTargets(final boolean force) { + if (force) { + logger.fine("Copy Texture Pbuffer used!"); + _useDirectRender = false; + _texture = null; + if (_pbuffer != null) { + try { + giveBackContext(); + } catch (final LWJGLException ex) { + } + ContextManager.removeContext(_pbuffer); + } + } else { + if ((Pbuffer.getCapabilities() & Pbuffer.RENDER_TEXTURE_SUPPORTED) != 0) { + logger.fine("Render to Texture Pbuffer supported!"); + if (_texture == null) { + logger.fine("No RenderTexture used in init, falling back to Copy Texture PBuffer."); + _useDirectRender = false; + } else { + _useDirectRender = true; + } + } else { + logger.fine("Copy Texture Pbuffer supported!"); + _texture = null; + } + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglRenderer.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglRenderer.java new file mode 100644 index 0000000..d856465 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglRenderer.java @@ -0,0 +1,1743 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.renderer.lwjgl; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.Collection; +import java.util.List; +import java.util.logging.Logger; + +import org.lwjgl.opengl.ARBBufferObject; +import org.lwjgl.opengl.ARBMultitexture; +import org.lwjgl.opengl.ARBPointParameters; +import org.lwjgl.opengl.ARBPointSprite; +import org.lwjgl.opengl.ARBVertexBufferObject; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.EXTFogCoord; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL31; +import org.lwjgl.opengl.OpenGLException; + +import com.ardor3d.image.ImageDataFormat; +import com.ardor3d.image.Texture; +import com.ardor3d.image.Texture1D; +import com.ardor3d.image.Texture2D; +import com.ardor3d.image.Texture3D; +import com.ardor3d.image.TextureCubeMap; +import com.ardor3d.image.TextureCubeMap.Face; +import com.ardor3d.math.Matrix4; +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.math.type.ReadOnlyRectangle2; +import com.ardor3d.math.type.ReadOnlyTransform; +import com.ardor3d.math.type.ReadOnlyVector3; +import com.ardor3d.renderer.AbstractRenderer; +import com.ardor3d.renderer.Camera; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.DrawBufferTarget; +import com.ardor3d.renderer.IndexMode; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.queue.RenderBucketType; +import com.ardor3d.renderer.state.BlendState; +import com.ardor3d.renderer.state.ClipState; +import com.ardor3d.renderer.state.ColorMaskState; +import com.ardor3d.renderer.state.CullState; +import com.ardor3d.renderer.state.FogState; +import com.ardor3d.renderer.state.FragmentProgramState; +import com.ardor3d.renderer.state.GLSLShaderObjectsState; +import com.ardor3d.renderer.state.LightState; +import com.ardor3d.renderer.state.MaterialState; +import com.ardor3d.renderer.state.OffsetState; +import com.ardor3d.renderer.state.RenderState; +import com.ardor3d.renderer.state.ShadingState; +import com.ardor3d.renderer.state.StencilState; +import com.ardor3d.renderer.state.TextureState; +import com.ardor3d.renderer.state.VertexProgramState; +import com.ardor3d.renderer.state.WireframeState; +import com.ardor3d.renderer.state.ZBufferState; +import com.ardor3d.renderer.state.record.LineRecord; +import com.ardor3d.renderer.state.record.RendererRecord; +import com.ardor3d.scene.state.lwjgl.LwjglBlendStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglClipStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglColorMaskStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglCullStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglFogStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglFragmentProgramStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglLightStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglMaterialStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglOffsetStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglShaderObjectsStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglShadingStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglStencilStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglTextureStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglVertexProgramStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglWireframeStateUtil; +import com.ardor3d.scene.state.lwjgl.LwjglZBufferStateUtil; +import com.ardor3d.scene.state.lwjgl.util.LwjglRendererUtil; +import com.ardor3d.scene.state.lwjgl.util.LwjglTextureUtil; +import com.ardor3d.scenegraph.AbstractBufferData; +import com.ardor3d.scenegraph.AbstractBufferData.VBOAccessMode; +import com.ardor3d.scenegraph.FloatBufferData; +import com.ardor3d.scenegraph.IndexBufferData; +import com.ardor3d.scenegraph.Mesh; +import com.ardor3d.scenegraph.Renderable; +import com.ardor3d.scenegraph.Spatial; +import com.ardor3d.scenegraph.hint.NormalsMode; +import com.ardor3d.util.Ardor3dException; +import com.ardor3d.util.Constants; +import com.ardor3d.util.geom.BufferUtils; +import com.ardor3d.util.stat.StatCollector; +import com.ardor3d.util.stat.StatType; + +/** + * LwjglRenderer provides an implementation of the Renderer interface using the LWJGL API. + * + * @see com.ardor3d.renderer.Renderer + */ +public class LwjglRenderer extends AbstractRenderer { + private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName()); + + private final FloatBuffer _transformBuffer = BufferUtils.createFloatBuffer(16); + private final Matrix4 _transformMatrix = new Matrix4(); + + /** + * Constructor instantiates a new LwjglRenderer object. + */ + public LwjglRenderer() { + logger.fine("LwjglRenderer created."); + } + + public void setBackgroundColor(final ReadOnlyColorRGBA color) { + _backgroundColor.set(color); + GL11.glClearColor(_backgroundColor.getRed(), _backgroundColor.getGreen(), _backgroundColor.getBlue(), + _backgroundColor.getAlpha()); + } + + @Override + public void renderBuckets() { + renderBuckets(true, true); + } + + @Override + public void renderBuckets(final boolean doSort, final boolean doClear) { + _processingQueue = true; + if (doSort && doClear) { + _queue.renderBuckets(this); + } else { + if (doSort) { + _queue.sortBuckets(); + } + _queue.renderOnly(this); + if (doClear) { + _queue.clearBuckets(); + } + } + _processingQueue = false; + } + + /** + * clear the render queue + */ + public void clearQueue() { + _queue.clearBuckets(); + } + + public void clearBuffers(final int buffers) { + clearBuffers(buffers, false); + } + + public void clearBuffers(final int buffers, final boolean strict) { + + int clear = 0; + + if ((buffers & Renderer.BUFFER_COLOR) != 0) { + clear |= GL11.GL_COLOR_BUFFER_BIT; + } + + if ((buffers & Renderer.BUFFER_DEPTH) != 0) { + clear |= GL11.GL_DEPTH_BUFFER_BIT; + + // make sure no funny business is going on in the z before clearing. + if (defaultStateList.containsKey(RenderState.StateType.ZBuffer)) { + defaultStateList.get(RenderState.StateType.ZBuffer).setNeedsRefresh(true); + doApplyState(defaultStateList.get(RenderState.StateType.ZBuffer)); + } + } + + if ((buffers & Renderer.BUFFER_STENCIL) != 0) { + clear |= GL11.GL_STENCIL_BUFFER_BIT; + + GL11.glClearStencil(_stencilClearValue); + GL11.glStencilMask(~0); + GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); + } + + if ((buffers & Renderer.BUFFER_ACCUMULATION) != 0) { + clear |= GL11.GL_ACCUM_BUFFER_BIT; + } + + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord record = context.getRendererRecord(); + + if (strict) { + // grab our camera to get width and height info. + final Camera cam = Camera.getCurrentCamera(); + + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(0, 0, cam.getWidth(), cam.getHeight()); + record.setClippingTestEnabled(true); + } + + GL11.glClear(clear); + + if (strict) { + // put us back. + LwjglRendererUtil.applyScissors(record); + } + } + + public void flushFrame(final boolean doSwap) { + renderBuckets(); + + GL11.glFlush(); + if (doSwap) { + doApplyState(defaultStateList.get(RenderState.StateType.ColorMask)); + + if (Constants.stats) { + StatCollector.startStat(StatType.STAT_DISPLAYSWAP_TIMER); + } + Display.update(); + if (Constants.stats) { + StatCollector.endStat(StatType.STAT_DISPLAYSWAP_TIMER); + } + } + + if (Constants.stats) { + StatCollector.addStat(StatType.STAT_FRAMES, 1); + } + } + + public void setOrtho() { + if (_inOrthoMode) { + throw new Ardor3dException("Already in Orthographic mode."); + } + // set up ortho mode + final RendererRecord matRecord = ContextManager.getCurrentContext().getRendererRecord(); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_PROJECTION); + GL11.glPushMatrix(); + GL11.glLoadIdentity(); + final Camera camera = Camera.getCurrentCamera(); + final double viewportWidth = camera.getWidth() * (camera.getViewPortRight() - camera.getViewPortLeft()); + final double viewportHeight = camera.getHeight() * (camera.getViewPortTop() - camera.getViewPortBottom()); + GL11.glOrtho(0, viewportWidth, 0, viewportHeight, -1, 1); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_MODELVIEW); + GL11.glPushMatrix(); + GL11.glLoadIdentity(); + _inOrthoMode = true; + } + + public void unsetOrtho() { + if (!_inOrthoMode) { + throw new Ardor3dException("Not in Orthographic mode."); + } + // remove ortho mode, and go back to original + // state + final RendererRecord matRecord = ContextManager.getCurrentContext().getRendererRecord(); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_PROJECTION); + GL11.glPopMatrix(); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_MODELVIEW); + GL11.glPopMatrix(); + _inOrthoMode = false; + } + + public void grabScreenContents(final ByteBuffer buff, final ImageDataFormat format, final int x, final int y, + final int w, final int h) { + final int pixFormat = LwjglTextureUtil.getGLPixelFormat(format); + GL11.glReadPixels(x, y, w, h, pixFormat, GL11.GL_UNSIGNED_BYTE, buff); + } + + public void draw(final Spatial s) { + if (s != null) { + s.onDraw(this); + } + } + + public boolean checkAndAdd(final Spatial s) { + final RenderBucketType rqMode = s.getSceneHints().getRenderBucketType(); + if (rqMode != RenderBucketType.Skip) { + getQueue().addToQueue(s, rqMode); + return true; + } + return false; + } + + /** + * re-initializes the GL context for rendering of another piece of geometry. + */ + protected void postdrawGeometry(final Mesh g) { + // Nothing to do here yet + } + + public void flushGraphics() { + GL11.glFlush(); + } + + public void finishGraphics() { + GL11.glFinish(); + } + + public void applyNormalsMode(final NormalsMode normalsMode, final ReadOnlyTransform worldTransform) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + if (normalsMode != NormalsMode.Off) { + final ContextCapabilities caps = context.getCapabilities(); + switch (normalsMode) { + case NormalizeIfScaled: + if (worldTransform.isRotationMatrix()) { + final ReadOnlyVector3 scale = worldTransform.getScale(); + if (!(scale.getX() == 1.0 && scale.getY() == 1.0 && scale.getZ() == 1.0)) { + if (scale.getX() == scale.getY() && scale.getY() == scale.getZ() + && caps.isOpenGL1_2Supported() + && rendRecord.getNormalMode() != GL12.GL_RESCALE_NORMAL) { + if (rendRecord.getNormalMode() == GL11.GL_NORMALIZE) { + GL11.glDisable(GL11.GL_NORMALIZE); + } + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + rendRecord.setNormalMode(GL12.GL_RESCALE_NORMAL); + } else if (rendRecord.getNormalMode() != GL11.GL_NORMALIZE) { + if (rendRecord.getNormalMode() == GL12.GL_RESCALE_NORMAL) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } + GL11.glEnable(GL11.GL_NORMALIZE); + rendRecord.setNormalMode(GL11.GL_NORMALIZE); + } + } else { + if (rendRecord.getNormalMode() == GL12.GL_RESCALE_NORMAL) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } else if (rendRecord.getNormalMode() == GL11.GL_NORMALIZE) { + GL11.glDisable(GL11.GL_NORMALIZE); + } + rendRecord.setNormalMode(GL11.GL_ZERO); + } + } else { + if (!worldTransform.getMatrix().isIdentity()) { + // *might* be scaled... + if (rendRecord.getNormalMode() != GL11.GL_NORMALIZE) { + if (rendRecord.getNormalMode() == GL12.GL_RESCALE_NORMAL) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } + GL11.glEnable(GL11.GL_NORMALIZE); + rendRecord.setNormalMode(GL11.GL_NORMALIZE); + } + } else { + // not scaled + if (rendRecord.getNormalMode() == GL12.GL_RESCALE_NORMAL) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } else if (rendRecord.getNormalMode() == GL11.GL_NORMALIZE) { + GL11.glDisable(GL11.GL_NORMALIZE); + } + rendRecord.setNormalMode(GL11.GL_ZERO); + } + } + break; + case AlwaysNormalize: + if (rendRecord.getNormalMode() != GL11.GL_NORMALIZE) { + if (rendRecord.getNormalMode() == GL12.GL_RESCALE_NORMAL) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } + GL11.glEnable(GL11.GL_NORMALIZE); + rendRecord.setNormalMode(GL11.GL_NORMALIZE); + } + break; + case UseProvided: + default: + if (rendRecord.getNormalMode() == GL12.GL_RESCALE_NORMAL) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } else if (rendRecord.getNormalMode() == GL11.GL_NORMALIZE) { + GL11.glDisable(GL11.GL_NORMALIZE); + } + rendRecord.setNormalMode(GL11.GL_ZERO); + break; + } + } else { + if (rendRecord.getNormalMode() == GL12.GL_RESCALE_NORMAL) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } else if (rendRecord.getNormalMode() == GL11.GL_NORMALIZE) { + GL11.glDisable(GL11.GL_NORMALIZE); + } + rendRecord.setNormalMode(GL11.GL_ZERO); + } + } + + public void applyDefaultColor(final ReadOnlyColorRGBA defaultColor) { + if (defaultColor != null) { + GL11.glColor4f(defaultColor.getRed(), defaultColor.getGreen(), defaultColor.getBlue(), + defaultColor.getAlpha()); + } else { + GL11.glColor4f(1, 1, 1, 1); + } + } + + public void deleteVBOs(final Collection ids) { + final IntBuffer idBuffer = BufferUtils.createIntBuffer(ids.size()); + idBuffer.clear(); + for (final Integer i : ids) { + if (i != null && i != 0) { + idBuffer.put(i); + } + } + idBuffer.flip(); + if (idBuffer.remaining() > 0) { + ARBBufferObject.glDeleteBuffersARB(idBuffer); + } + } + + public void deleteDisplayLists(final Collection ids) { + for (final Integer i : ids) { + if (i != null && i != 0) { + System.err.println("deleted DL: " + i); + GL11.glDeleteLists(i, 1); + } + } + } + + public void deleteVBOs(final AbstractBufferData buffer) { + if (buffer == null) { + return; + } + + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + + final int id = buffer.getVBOID(context.getGlContextRep()); + if (id == 0) { + // Not on card... return. + return; + } + + buffer.removeVBOID(context.getGlContextRep()); + + final IntBuffer idBuff = BufferUtils.createIntBuffer(1); + idBuff.put(id); + idBuff.flip(); + ARBBufferObject.glDeleteBuffersARB(idBuff); + } + + public void updateTexture1DSubImage(final Texture1D destination, final int dstOffsetX, final int dstWidth, + final ByteBuffer source, final int srcOffsetX) { + updateTexSubImage(destination, dstOffsetX, 0, 0, dstWidth, 0, 0, source, srcOffsetX, 0, 0, 0, 0, null); + } + + public void updateTexture2DSubImage(final Texture2D destination, final int dstOffsetX, final int dstOffsetY, + final int dstWidth, final int dstHeight, final ByteBuffer source, final int srcOffsetX, + final int srcOffsetY, final int srcTotalWidth) { + updateTexSubImage(destination, dstOffsetX, dstOffsetY, 0, dstWidth, dstHeight, 0, source, srcOffsetX, + srcOffsetY, 0, srcTotalWidth, 0, null); + } + + public void updateTexture3DSubImage(final Texture3D destination, final int dstOffsetX, final int dstOffsetY, + final int dstOffsetZ, final int dstWidth, final int dstHeight, final int dstDepth, final ByteBuffer source, + final int srcOffsetX, final int srcOffsetY, final int srcOffsetZ, final int srcTotalWidth, + final int srcTotalHeight) { + updateTexSubImage(destination, dstOffsetX, dstOffsetY, dstOffsetZ, dstWidth, dstHeight, dstDepth, source, + srcOffsetX, srcOffsetY, srcOffsetZ, srcTotalWidth, srcTotalHeight, null); + } + + public void updateTextureCubeMapSubImage(final TextureCubeMap destination, final TextureCubeMap.Face dstFace, + final int dstOffsetX, final int dstOffsetY, final int dstWidth, final int dstHeight, + final ByteBuffer source, final int srcOffsetX, final int srcOffsetY, final int srcTotalWidth) { + updateTexSubImage(destination, dstOffsetX, dstOffsetY, 0, dstWidth, dstHeight, 0, source, srcOffsetX, + srcOffsetY, 0, srcTotalWidth, 0, dstFace); + } + + private void updateTexSubImage(final Texture destination, final int dstOffsetX, final int dstOffsetY, + final int dstOffsetZ, final int dstWidth, final int dstHeight, final int dstDepth, final ByteBuffer source, + final int srcOffsetX, final int srcOffsetY, final int srcOffsetZ, final int srcTotalWidth, + final int srcTotalHeight, final Face dstFace) { + + // Ignore textures that do not have an id set + if (destination.getTextureIdForContext(ContextManager.getCurrentContext().getGlContextRep()) == 0) { + logger.warning("Attempting to update a texture that is not currently on the card."); + return; + } + + // Determine the original texture configuration, so that this method can + // restore the texture configuration to its original state. + final IntBuffer idBuff = BufferUtils.createIntBuffer(16); + GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT, idBuff); + final int origAlignment = idBuff.get(0); + final int origRowLength = 0; + final int origImageHeight = 0; + final int origSkipPixels = 0; + final int origSkipRows = 0; + final int origSkipImages = 0; + + final int alignment = 1; + + int rowLength; + if (srcTotalWidth == dstWidth) { + // When the row length is zero, then the width parameter is used. + // We use zero in these cases in the hope that we can avoid two + // unnecessary calls to glPixelStorei. + rowLength = 0; + } else { + // The number of pixels in a row is different than the number of + // pixels in the region to be uploaded to the texture. + rowLength = srcTotalWidth; + } + + int imageHeight; + if (srcTotalHeight == dstHeight) { + // When the image height is zero, then the height parameter is used. + // We use zero in these cases in the hope that we can avoid two + // unnecessary calls to glPixelStorei. + imageHeight = 0; + } else { + // The number of pixels in a row is different than the number of + // pixels in the region to be uploaded to the texture. + imageHeight = srcTotalHeight; + } + + // Grab pixel format + final int pixelFormat; + if (destination.getImage() != null) { + pixelFormat = LwjglTextureUtil.getGLPixelFormat(destination.getImage().getDataFormat()); + } else { + pixelFormat = LwjglTextureUtil.getGLPixelFormatFromStoreFormat(destination.getTextureStoreFormat()); + } + + // bind... + LwjglTextureStateUtil.doTextureBind(destination, 0, false); + + // Update the texture configuration (when necessary). + + if (origAlignment != alignment) { + GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, alignment); + } + if (origRowLength != rowLength) { + GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, rowLength); + } + if (origSkipPixels != srcOffsetX) { + GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, srcOffsetX); + } + // NOTE: The below will be skipped for texture types that don't support them because we are passing in 0's. + if (origSkipRows != srcOffsetY) { + GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, srcOffsetY); + } + if (origImageHeight != imageHeight) { + GL11.glPixelStorei(GL12.GL_UNPACK_IMAGE_HEIGHT, imageHeight); + } + if (origSkipImages != srcOffsetZ) { + GL11.glPixelStorei(GL12.GL_UNPACK_SKIP_IMAGES, srcOffsetZ); + } + + // Upload the image region into the texture. + try { + switch (destination.getType()) { + case TwoDimensional: + GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, dstOffsetX, dstOffsetY, dstWidth, dstHeight, + pixelFormat, GL11.GL_UNSIGNED_BYTE, source); + break; + case OneDimensional: + GL11.glTexSubImage1D(GL11.GL_TEXTURE_1D, 0, dstOffsetX, dstWidth, pixelFormat, + GL11.GL_UNSIGNED_BYTE, source); + break; + case ThreeDimensional: + GL12.glTexSubImage3D(GL12.GL_TEXTURE_3D, 0, dstOffsetX, dstOffsetY, dstOffsetZ, dstWidth, + dstHeight, dstDepth, pixelFormat, GL11.GL_UNSIGNED_BYTE, source); + break; + case CubeMap: + GL11.glTexSubImage2D(LwjglTextureStateUtil.getGLCubeMapFace(dstFace), 0, dstOffsetX, dstOffsetY, + dstWidth, dstHeight, pixelFormat, GL11.GL_UNSIGNED_BYTE, source); + break; + default: + throw new Ardor3dException("Unsupported type for updateTextureSubImage: " + destination.getType()); + } + } finally { + // Restore the texture configuration (when necessary)... + // Restore alignment. + if (origAlignment != alignment) { + GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, origAlignment); + } + // Restore row length. + if (origRowLength != rowLength) { + GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, origRowLength); + } + // Restore skip pixels. + if (origSkipPixels != srcOffsetX) { + GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, origSkipPixels); + } + // Restore skip rows. + if (origSkipRows != srcOffsetY) { + GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, origSkipRows); + } + // Restore image height. + if (origImageHeight != imageHeight) { + GL11.glPixelStorei(GL12.GL_UNPACK_IMAGE_HEIGHT, origImageHeight); + } + // Restore skip images. + if (origSkipImages != srcOffsetZ) { + GL11.glPixelStorei(GL12.GL_UNPACK_SKIP_IMAGES, origSkipImages); + } + } + } + + public void checkCardError() throws Ardor3dException { + try { + org.lwjgl.opengl.Util.checkGLError(); + } catch (final OpenGLException exception) { + throw new Ardor3dException("Error in opengl: " + exception.getMessage(), exception); + } + } + + public void draw(final Renderable renderable) { + if (renderLogic != null) { + renderLogic.apply(renderable); + } + renderable.render(this); + if (renderLogic != null) { + renderLogic.restore(renderable); + } + } + + public boolean doTransforms(final ReadOnlyTransform transform) { + // set world matrix + if (!transform.isIdentity()) { + synchronized (_transformMatrix) { + transform.getGLApplyMatrix(_transformBuffer); + + final RendererRecord matRecord = ContextManager.getCurrentContext().getRendererRecord(); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_MODELVIEW); + GL11.glPushMatrix(); + GL11.glMultMatrix(_transformBuffer); + return true; + } + } + return false; + } + + public void undoTransforms(final ReadOnlyTransform transform) { + final RendererRecord matRecord = ContextManager.getCurrentContext().getRendererRecord(); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_MODELVIEW); + GL11.glPopMatrix(); + } + + public void setupVertexData(final FloatBufferData vertexBufferData) { + final FloatBuffer vertexBuffer = vertexBufferData != null ? vertexBufferData.getBuffer() : null; + + if (vertexBuffer == null) { + GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY); + } else { + GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY); + vertexBuffer.rewind(); + GL11.glVertexPointer(vertexBufferData.getValuesPerTuple(), 0, vertexBuffer); + } + } + + public void setupNormalData(final FloatBufferData normalBufferData) { + final FloatBuffer normalBuffer = normalBufferData != null ? normalBufferData.getBuffer() : null; + + if (normalBuffer == null) { + GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY); + } else { + GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY); + normalBuffer.rewind(); + GL11.glNormalPointer(0, normalBuffer); + } + } + + public void setupColorData(final FloatBufferData colorBufferData) { + final FloatBuffer colorBuffer = colorBufferData != null ? colorBufferData.getBuffer() : null; + + if (colorBuffer == null) { + GL11.glDisableClientState(GL11.GL_COLOR_ARRAY); + } else { + GL11.glEnableClientState(GL11.GL_COLOR_ARRAY); + colorBuffer.rewind(); + GL11.glColorPointer(colorBufferData.getValuesPerTuple(), 0, colorBuffer); + } + } + + public void setupFogData(final FloatBufferData fogBufferData) { + final FloatBuffer fogBuffer = fogBufferData != null ? fogBufferData.getBuffer() : null; + + if (fogBuffer == null) { + GL11.glDisableClientState(EXTFogCoord.GL_FOG_COORDINATE_ARRAY_EXT); + } else { + GL11.glEnableClientState(EXTFogCoord.GL_FOG_COORDINATE_ARRAY_EXT); + fogBuffer.rewind(); + EXTFogCoord.glFogCoordPointerEXT(0, fogBuffer); + } + } + + public void setupTextureData(final List textureCoords) { + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + final RendererRecord rendRecord = context.getRendererRecord(); + + final TextureState ts = (TextureState) context.getCurrentState(RenderState.StateType.Texture); + int enabledTextures = rendRecord.getEnabledTextures(); + final boolean valid = rendRecord.isTexturesValid(); + boolean isOn, wasOn; + if (ts != null) { + final int max = caps.isMultitextureSupported() ? Math.min(caps.getNumberOfFragmentTexCoordUnits(), + TextureState.MAX_TEXTURES) : 1; + for (int i = 0; i < max; i++) { + wasOn = (enabledTextures & (2 << i)) != 0; + isOn = textureCoords != null && i < textureCoords.size() && textureCoords.get(i) != null + && textureCoords.get(i).getBuffer() != null; + + if (!isOn) { + if (valid && !wasOn) { + continue; + } else { + checkAndSetTextureArrayUnit(i, rendRecord, caps); + + // disable bit in tracking int + enabledTextures &= ~(2 << i); + + // disable state + GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + + continue; + } + } else { + checkAndSetTextureArrayUnit(i, rendRecord, caps); + + if (!valid || !wasOn) { + // enable state + GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + + // enable bit in tracking int + enabledTextures |= (2 << i); + } + + final FloatBufferData textureBufferData = textureCoords.get(i); + final FloatBuffer textureBuffer = textureBufferData.getBuffer(); + + GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + textureBuffer.rewind(); + GL11.glTexCoordPointer(textureBufferData.getValuesPerTuple(), 0, textureBuffer); + } + } + } + + rendRecord.setEnabledTextures(enabledTextures); + rendRecord.setTexturesValid(true); + } + + @Override + public void drawElements(final IndexBufferData indices, final int[] indexLengths, final IndexMode[] indexModes, + final int primcount) { + if (indices == null || indices.getBuffer() == null) { + logger.severe("Missing indices for drawElements call without VBO"); + return; + } + + if (indexLengths == null) { + final int glIndexMode = getGLIndexMode(indexModes[0]); + + indices.position(0); + if (indices.getBuffer() instanceof IntBuffer) { + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, (IntBuffer) indices.getBuffer()); + } else { + GL31.glDrawElementsInstanced(glIndexMode, (IntBuffer) indices.getBuffer(), primcount); + } + } else if (indices.getBuffer() instanceof ShortBuffer) { + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, (ShortBuffer) indices.getBuffer()); + } else { + GL31.glDrawElementsInstanced(glIndexMode, (ShortBuffer) indices.getBuffer(), primcount); + } + + } else if (indices.getBuffer() instanceof ByteBuffer) { + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, (ByteBuffer) indices.getBuffer()); + } else { + GL31.glDrawElementsInstanced(glIndexMode, (ByteBuffer) indices.getBuffer(), primcount); + } + } + + if (Constants.stats) { + addStats(indexModes[0], indices.getBufferLimit()); + } + } else { + int offset = 0; + int indexModeCounter = 0; + for (int i = 0; i < indexLengths.length; i++) { + final int count = indexLengths[i]; + + final int glIndexMode = getGLIndexMode(indexModes[indexModeCounter]); + + indices.getBuffer().position(offset); + indices.getBuffer().limit(offset + count); + if (indices.getBuffer() instanceof IntBuffer) { + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, (IntBuffer) indices.getBuffer()); + } else { + GL31.glDrawElementsInstanced(glIndexMode, (IntBuffer) indices.getBuffer(), primcount); + } + + } else if (indices.getBuffer() instanceof ShortBuffer) { + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, (ShortBuffer) indices.getBuffer()); + } else { + GL31.glDrawElementsInstanced(glIndexMode, (ShortBuffer) indices.getBuffer(), primcount); + } + + } else if (indices.getBuffer() instanceof ByteBuffer) { + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, (ByteBuffer) indices.getBuffer()); + } else { + GL31.glDrawElementsInstanced(glIndexMode, (ByteBuffer) indices.getBuffer(), primcount); + } + } + + if (Constants.stats) { + addStats(indexModes[indexModeCounter], count); + } + + offset += count; + + if (indexModeCounter < indexModes.length - 1) { + indexModeCounter++; + } + } + } + } + + public static int setupVBO(final AbstractBufferData data, final RenderContext context) { + if (data == null) { + return 0; + } + + final RendererRecord rendRecord = context.getRendererRecord(); + + int vboID = data.getVBOID(context.getGlContextRep()); + if (vboID != 0) { + updateVBO(data, rendRecord, vboID, 0); + + return vboID; + } + + final Buffer dataBuffer = data.getBuffer(); + if (dataBuffer != null) { + // XXX: should we be rewinding? Maybe make that the programmer's responsibility. + dataBuffer.rewind(); + vboID = makeVBOId(); + data.setVBOID(context.getGlContextRep(), vboID); + + rendRecord.invalidateVBO(); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + if (dataBuffer instanceof FloatBuffer) { + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (FloatBuffer) dataBuffer, + getGLVBOAccessMode(data.getVboAccessMode())); + } else if (dataBuffer instanceof ByteBuffer) { + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (ByteBuffer) dataBuffer, + getGLVBOAccessMode(data.getVboAccessMode())); + } else if (dataBuffer instanceof IntBuffer) { + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (IntBuffer) dataBuffer, + getGLVBOAccessMode(data.getVboAccessMode())); + } else if (dataBuffer instanceof ShortBuffer) { + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, (ShortBuffer) dataBuffer, + getGLVBOAccessMode(data.getVboAccessMode())); + } + } else { + throw new Ardor3dException("Attempting to create a vbo id for a FloatBufferData with no Buffer value."); + } + return vboID; + } + + public static void updateVBO(final AbstractBufferData data, final RendererRecord rendRecord, + final int vboID, final int offsetBytes) { + if (data.isNeedsRefresh()) { + final Buffer dataBuffer = data.getBuffer(); + dataBuffer.rewind(); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + if (dataBuffer instanceof FloatBuffer) { + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + (FloatBuffer) dataBuffer); + } else if (dataBuffer instanceof ByteBuffer) { + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + (ByteBuffer) dataBuffer); + } else if (dataBuffer instanceof IntBuffer) { + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + (IntBuffer) dataBuffer); + } else if (dataBuffer instanceof ShortBuffer) { + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + (ShortBuffer) dataBuffer); + } + data.setNeedsRefresh(false); + } + } + + private int setupIndicesVBO(final IndexBufferData data, final RenderContext context, + final RendererRecord rendRecord) { + if (data == null) { + return 0; + } + + int vboID = data.getVBOID(context.getGlContextRep()); + if (vboID != 0) { + if (data.isNeedsRefresh()) { + final Buffer dataBuffer = data.getBuffer(); + dataBuffer.rewind(); + LwjglRendererUtil.setBoundElementVBO(rendRecord, vboID); + if (dataBuffer instanceof IntBuffer) { + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0, + (IntBuffer) dataBuffer); + } else if (dataBuffer instanceof ShortBuffer) { + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0, + (ShortBuffer) dataBuffer); + } else if (dataBuffer instanceof ByteBuffer) { + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0, + (ByteBuffer) dataBuffer); + } + data.setNeedsRefresh(false); + } + + return vboID; + } + + final Buffer dataBuffer = data.getBuffer(); + if (dataBuffer != null) { + // XXX: should we be rewinding? Maybe make that the programmer's responsibility. + dataBuffer.rewind(); + vboID = makeVBOId(); + data.setVBOID(context.getGlContextRep(), vboID); + + rendRecord.invalidateVBO(); + LwjglRendererUtil.setBoundElementVBO(rendRecord, vboID); + if (dataBuffer instanceof IntBuffer) { + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, + (IntBuffer) dataBuffer, getGLVBOAccessMode(data.getVboAccessMode())); + } else if (dataBuffer instanceof ShortBuffer) { + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, + (ShortBuffer) dataBuffer, getGLVBOAccessMode(data.getVboAccessMode())); + } else if (dataBuffer instanceof ByteBuffer) { + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, + (ByteBuffer) dataBuffer, getGLVBOAccessMode(data.getVboAccessMode())); + } + } else { + throw new Ardor3dException("Attempting to create a vbo id for a IndexBufferData with no Buffer value."); + } + return vboID; + } + + public void setupVertexDataVBO(final FloatBufferData data) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + + final int vboID = setupVBO(data, context); + + if (vboID != 0) { + GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + GL11.glVertexPointer(data.getValuesPerTuple(), GL11.GL_FLOAT, 0, 0); + } else { + GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY); + } + } + + public void setupNormalDataVBO(final FloatBufferData data) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + + final int vboID = setupVBO(data, context); + + if (vboID != 0) { + GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + GL11.glNormalPointer(GL11.GL_FLOAT, 0, 0); + } else { + GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY); + } + } + + public void setupColorDataVBO(final FloatBufferData data) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + + final int vboID = setupVBO(data, context); + + if (vboID != 0) { + GL11.glEnableClientState(GL11.GL_COLOR_ARRAY); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + GL11.glColorPointer(data.getValuesPerTuple(), GL11.GL_FLOAT, 0, 0); + } else { + GL11.glDisableClientState(GL11.GL_COLOR_ARRAY); + } + } + + public void setupFogDataVBO(final FloatBufferData data) { + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + + if (!caps.isFogCoordinatesSupported()) { + return; + } + + final RendererRecord rendRecord = context.getRendererRecord(); + final int vboID = setupVBO(data, context); + + if (vboID != 0) { + GL11.glEnableClientState(EXTFogCoord.GL_FOG_COORDINATE_ARRAY_EXT); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + EXTFogCoord.glFogCoordPointerEXT(GL11.GL_FLOAT, 0, 0); + } else { + GL11.glDisableClientState(EXTFogCoord.GL_FOG_COORDINATE_ARRAY_EXT); + } + } + + public void setupTextureDataVBO(final List textureCoords) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + final ContextCapabilities caps = context.getCapabilities(); + + final TextureState ts = (TextureState) context.getCurrentState(RenderState.StateType.Texture); + int enabledTextures = rendRecord.getEnabledTextures(); + final boolean valid = rendRecord.isTexturesValid(); + boolean exists, wasOn; + if (ts != null) { + final int max = caps.isMultitextureSupported() ? Math.min(caps.getNumberOfFragmentTexCoordUnits(), + TextureState.MAX_TEXTURES) : 1; + for (int i = 0; i < max; i++) { + wasOn = (enabledTextures & (2 << i)) != 0; + exists = textureCoords != null && i < textureCoords.size(); + + if (!exists) { + if (valid && !wasOn) { + continue; + } else { + checkAndSetTextureArrayUnit(i, rendRecord, caps); + + // disable bit in tracking int + enabledTextures &= ~(2 << i); + + // disable state + GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + + continue; + } + } else { + checkAndSetTextureArrayUnit(i, rendRecord, caps); + + // grab a vboID and make sure it exists and is up to date. + final FloatBufferData data = textureCoords.get(i); + final int vboID = setupVBO(data, context); + + // Found good vbo + if (vboID != 0) { + if (!valid || !wasOn) { + // enable bit in tracking int + enabledTextures |= (2 << i); + + // enable state + GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + } + + // set our active vbo + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + + // send data + GL11.glTexCoordPointer(data.getValuesPerTuple(), GL11.GL_FLOAT, 0, 0); + } + // Not a good vbo, disable it. + else { + if (!valid || wasOn) { + // disable bit in tracking int + enabledTextures &= ~(2 << i); + + // disable state + GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + } + } + } + } + } + + rendRecord.setEnabledTextures(enabledTextures); + rendRecord.setTexturesValid(true); + } + + public void setupInterleavedDataVBO(final FloatBufferData interleaved, final FloatBufferData vertexCoords, + final FloatBufferData normalCoords, final FloatBufferData colorCoords, + final List textureCoords) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + final ContextCapabilities caps = context.getCapabilities(); + + final int lengthBytes = getTotalInterleavedSize(context, vertexCoords, normalCoords, colorCoords, textureCoords); + int currLengthBytes = 0; + if (interleaved.getBufferLimit() > 0) { + interleaved.getBuffer().rewind(); + currLengthBytes = Math.round(interleaved.getBuffer().get()); + } + + if (lengthBytes != currLengthBytes || interleaved.getVBOID(context.getGlContextRep()) == 0 + || interleaved.isNeedsRefresh()) { + initializeInterleavedVBO(context, interleaved, vertexCoords, normalCoords, colorCoords, textureCoords, + lengthBytes); + } + + final int vboID = interleaved.getVBOID(context.getGlContextRep()); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + + int offsetBytes = 0; + + if (normalCoords != null) { + updateVBO(normalCoords, rendRecord, vboID, offsetBytes); + GL11.glNormalPointer(GL11.GL_FLOAT, 0, offsetBytes); + GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY); + offsetBytes += normalCoords.getBufferLimit() * 4; + } else { + GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY); + } + + if (colorCoords != null) { + updateVBO(colorCoords, rendRecord, vboID, offsetBytes); + GL11.glColorPointer(colorCoords.getValuesPerTuple(), GL11.GL_FLOAT, 0, offsetBytes); + GL11.glEnableClientState(GL11.GL_COLOR_ARRAY); + offsetBytes += colorCoords.getBufferLimit() * 4; + } else { + GL11.glDisableClientState(GL11.GL_COLOR_ARRAY); + } + + if (textureCoords != null) { + final TextureState ts = (TextureState) context.getCurrentState(RenderState.StateType.Texture); + int enabledTextures = rendRecord.getEnabledTextures(); + final boolean valid = rendRecord.isTexturesValid(); + boolean exists, wasOn; + if (ts != null) { + final int max = caps.isMultitextureSupported() ? Math.min(caps.getNumberOfFragmentTexCoordUnits(), + TextureState.MAX_TEXTURES) : 1; + for (int i = 0; i < max; i++) { + wasOn = (enabledTextures & (2 << i)) != 0; + exists = textureCoords != null && i < textureCoords.size() && textureCoords.get(i) != null + && i <= ts.getMaxTextureIndexUsed(); + + if (!exists) { + if (valid && !wasOn) { + continue; + } else { + checkAndSetTextureArrayUnit(i, rendRecord, caps); + + // disable bit in tracking int + enabledTextures &= ~(2 << i); + + // disable state + GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + + continue; + } + + } else { + checkAndSetTextureArrayUnit(i, rendRecord, caps); + + // grab a vboID and make sure it exists and is up to date. + final FloatBufferData textureBufferData = textureCoords.get(i); + updateVBO(textureBufferData, rendRecord, vboID, offsetBytes); + + if (!valid || !wasOn) { + // enable bit in tracking int + enabledTextures |= (2 << i); + + // enable state + GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + } + + // send data + GL11.glTexCoordPointer(textureBufferData.getValuesPerTuple(), GL11.GL_FLOAT, 0, offsetBytes); + offsetBytes += textureBufferData.getBufferLimit() * 4; + } + } + } + + rendRecord.setEnabledTextures(enabledTextures); + rendRecord.setTexturesValid(true); + } + + if (vertexCoords != null) { + updateVBO(vertexCoords, rendRecord, vboID, offsetBytes); + GL11.glVertexPointer(vertexCoords.getValuesPerTuple(), GL11.GL_FLOAT, 0, offsetBytes); + GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY); + } else { + GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY); + } + } + + private void initializeInterleavedVBO(final RenderContext context, final FloatBufferData interleaved, + final FloatBufferData vertexCoords, final FloatBufferData normalCoords, final FloatBufferData colorCoords, + final List textureCoords, final int bufferSize) { + + // keep around buffer size + if (interleaved.getBufferCapacity() != 1) { + final FloatBuffer buffer = BufferUtils.createFloatBufferOnHeap(1); + interleaved.setBuffer(buffer); + } + interleaved.getBuffer().rewind(); + interleaved.getBuffer().put(bufferSize); + + final RendererRecord rendRecord = context.getRendererRecord(); + final ContextCapabilities caps = context.getCapabilities(); + + final int vboID = makeVBOId(); + interleaved.setVBOID(context.getGlContextRep(), vboID); + + rendRecord.invalidateVBO(); + LwjglRendererUtil.setBoundVBO(rendRecord, vboID); + ARBBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, bufferSize, + getGLVBOAccessMode(interleaved.getVboAccessMode())); + + int offsetBytes = 0; + if (normalCoords != null) { + normalCoords.getBuffer().rewind(); + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + normalCoords.getBuffer()); + offsetBytes += normalCoords.getBufferLimit() * 4; + } + if (colorCoords != null) { + colorCoords.getBuffer().rewind(); + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + colorCoords.getBuffer()); + offsetBytes += colorCoords.getBufferLimit() * 4; + } + if (textureCoords != null) { + final TextureState ts = (TextureState) context.getCurrentState(RenderState.StateType.Texture); + if (ts != null) { + for (int i = 0; i <= ts.getMaxTextureIndexUsed() && i < caps.getNumberOfFragmentTexCoordUnits(); i++) { + if (textureCoords == null || i >= textureCoords.size()) { + continue; + } + + final FloatBufferData textureBufferData = textureCoords.get(i); + final FloatBuffer textureBuffer = textureBufferData != null ? textureBufferData.getBuffer() : null; + if (textureBuffer != null) { + textureBuffer.rewind(); + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + textureBuffer); + offsetBytes += textureBufferData.getBufferLimit() * 4; + } + } + } + } + if (vertexCoords != null) { + vertexCoords.getBuffer().rewind(); + ARBBufferObject.glBufferSubDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, offsetBytes, + vertexCoords.getBuffer()); + } + + interleaved.setNeedsRefresh(false); + } + + @Override + public void drawElementsVBO(final IndexBufferData indices, final int[] indexLengths, + final IndexMode[] indexModes, final int primcount) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + + final int vboID = setupIndicesVBO(indices, context, rendRecord); + + LwjglRendererUtil.setBoundElementVBO(rendRecord, vboID); + + if (indexLengths == null) { + final int glIndexMode = getGLIndexMode(indexModes[0]); + + final int type = getGLDataType(indices); + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, indices.getBufferLimit(), type, 0); + } else { + GL31.glDrawElementsInstanced(glIndexMode, indices.getBufferLimit(), type, 0, primcount); + } + if (Constants.stats) { + addStats(indexModes[0], indices.getBufferLimit()); + } + } else { + int offset = 0; + int indexModeCounter = 0; + for (int i = 0; i < indexLengths.length; i++) { + final int count = indexLengths[i]; + + final int glIndexMode = getGLIndexMode(indexModes[indexModeCounter]); + + final int type = getGLDataType(indices); + final int byteSize = indices.getByteCount(); + // offset in this call is done in bytes. + if (primcount < 0) { + GL11.glDrawElements(glIndexMode, count, type, offset * byteSize); + } else { + GL31.glDrawElementsInstanced(glIndexMode, count, type, offset * byteSize, primcount); + } + + if (Constants.stats) { + addStats(indexModes[indexModeCounter], count); + } + + offset += count; + + if (indexModeCounter < indexModes.length - 1) { + indexModeCounter++; + } + } + } + } + + @Override + public void drawArrays(final FloatBufferData vertices, final int[] indexLengths, final IndexMode[] indexModes, + final int primcount) { + if (indexLengths == null) { + final int glIndexMode = getGLIndexMode(indexModes[0]); + + if (primcount < 0) { + GL11.glDrawArrays(glIndexMode, 0, vertices.getTupleCount()); + } else { + GL31.glDrawArraysInstanced(glIndexMode, 0, vertices.getTupleCount(), primcount); + } + + if (Constants.stats) { + addStats(indexModes[0], vertices.getTupleCount()); + } + } else { + int offset = 0; + int indexModeCounter = 0; + for (int i = 0; i < indexLengths.length; i++) { + final int count = indexLengths[i]; + + final int glIndexMode = getGLIndexMode(indexModes[indexModeCounter]); + + if (primcount < 0) { + GL11.glDrawArrays(glIndexMode, offset, count); + } else { + GL31.glDrawArraysInstanced(glIndexMode, offset, count, primcount); + } + + if (Constants.stats) { + addStats(indexModes[indexModeCounter], count); + } + + offset += count; + + if (indexModeCounter < indexModes.length - 1) { + indexModeCounter++; + } + } + } + } + + public static int makeVBOId() { + final IntBuffer idBuff = BufferUtils.createIntBuffer(1); + ARBBufferObject.glGenBuffersARB(idBuff); + return idBuff.get(0); + } + + public void unbindVBO() { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord rendRecord = context.getRendererRecord(); + LwjglRendererUtil.setBoundVBO(rendRecord, 0); + LwjglRendererUtil.setBoundElementVBO(rendRecord, 0); + } + + private static int getGLVBOAccessMode(final VBOAccessMode vboAccessMode) { + int glMode = ARBBufferObject.GL_STATIC_DRAW_ARB; + switch (vboAccessMode) { + case StaticDraw: + glMode = ARBBufferObject.GL_STATIC_DRAW_ARB; + break; + case StaticRead: + glMode = ARBBufferObject.GL_STATIC_READ_ARB; + break; + case StaticCopy: + glMode = ARBBufferObject.GL_STATIC_COPY_ARB; + break; + case DynamicDraw: + glMode = ARBBufferObject.GL_DYNAMIC_DRAW_ARB; + break; + case DynamicRead: + glMode = ARBBufferObject.GL_DYNAMIC_READ_ARB; + break; + case DynamicCopy: + glMode = ARBBufferObject.GL_DYNAMIC_COPY_ARB; + break; + case StreamDraw: + glMode = ARBBufferObject.GL_STREAM_DRAW_ARB; + break; + case StreamRead: + glMode = ARBBufferObject.GL_STREAM_READ_ARB; + break; + case StreamCopy: + glMode = ARBBufferObject.GL_STREAM_COPY_ARB; + break; + } + return glMode; + } + + private int getGLIndexMode(final IndexMode indexMode) { + int glMode = GL11.GL_TRIANGLES; + switch (indexMode) { + case Triangles: + glMode = GL11.GL_TRIANGLES; + break; + case TriangleStrip: + glMode = GL11.GL_TRIANGLE_STRIP; + break; + case TriangleFan: + glMode = GL11.GL_TRIANGLE_FAN; + break; + case Quads: + glMode = GL11.GL_QUADS; + break; + case QuadStrip: + glMode = GL11.GL_QUAD_STRIP; + break; + case Lines: + glMode = GL11.GL_LINES; + break; + case LineStrip: + glMode = GL11.GL_LINE_STRIP; + break; + case LineLoop: + glMode = GL11.GL_LINE_LOOP; + break; + case Points: + glMode = GL11.GL_POINTS; + break; + } + return glMode; + } + + private int getGLDataType(final IndexBufferData indices) { + if (indices.getBuffer() instanceof ByteBuffer) { + return GL11.GL_UNSIGNED_BYTE; + } else if (indices.getBuffer() instanceof ShortBuffer) { + return GL11.GL_UNSIGNED_SHORT; + } else if (indices.getBuffer() instanceof IntBuffer) { + return GL11.GL_UNSIGNED_INT; + } + + throw new IllegalArgumentException("Unknown buffer type: " + indices.getBuffer()); + } + + public void setModelViewMatrix(final FloatBuffer matrix) { + final RendererRecord matRecord = ContextManager.getCurrentContext().getRendererRecord(); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_MODELVIEW); + loadMatrix(matrix); + } + + public void setProjectionMatrix(final FloatBuffer matrix) { + final RendererRecord matRecord = ContextManager.getCurrentContext().getRendererRecord(); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_PROJECTION); + loadMatrix(matrix); + } + + private void loadMatrix(final FloatBuffer matrix) { + GL11.glLoadMatrix(matrix); + } + + public FloatBuffer getModelViewMatrix(final FloatBuffer store) { + return getMatrix(GL11.GL_MODELVIEW_MATRIX, store); + } + + public FloatBuffer getProjectionMatrix(final FloatBuffer store) { + return getMatrix(GL11.GL_PROJECTION_MATRIX, store); + } + + private FloatBuffer getMatrix(final int matrixType, final FloatBuffer store) { + FloatBuffer result = store; + if (result.remaining() < 16) { + result = BufferUtils.createFloatBuffer(16); + } + GL11.glGetFloat(matrixType, store); + return result; + } + + public void setViewport(final int x, final int y, final int width, final int height) { + GL11.glViewport(x, y, width, height); + } + + public void setDepthRange(final double depthRangeNear, final double depthRangeFar) { + GL11.glDepthRange(depthRangeNear, depthRangeFar); + } + + public void setDrawBuffer(final DrawBufferTarget target) { + final RendererRecord record = ContextManager.getCurrentContext().getRendererRecord(); + if (record.getDrawBufferTarget() != target) { + int buffer = GL11.GL_BACK; + switch (target) { + case Back: + break; + case Front: + buffer = GL11.GL_FRONT; + break; + case BackLeft: + buffer = GL11.GL_BACK_LEFT; + break; + case BackRight: + buffer = GL11.GL_BACK_RIGHT; + break; + case FrontLeft: + buffer = GL11.GL_FRONT_LEFT; + break; + case FrontRight: + buffer = GL11.GL_FRONT_RIGHT; + break; + case FrontAndBack: + buffer = GL11.GL_FRONT_AND_BACK; + break; + case Left: + buffer = GL11.GL_LEFT; + break; + case Right: + buffer = GL11.GL_RIGHT; + break; + case Aux0: + buffer = GL11.GL_AUX0; + break; + case Aux1: + buffer = GL11.GL_AUX1; + break; + case Aux2: + buffer = GL11.GL_AUX2; + break; + case Aux3: + buffer = GL11.GL_AUX3; + break; + } + + GL11.glDrawBuffer(buffer); + record.setDrawBufferTarget(target); + } + } + + public void setupLineParameters(final float lineWidth, final int stippleFactor, final short stipplePattern, + final boolean antialiased) { + final LineRecord lineRecord = ContextManager.getCurrentContext().getLineRecord(); + + if (!lineRecord.isValid() || lineRecord.width != lineWidth) { + GL11.glLineWidth(lineWidth); + lineRecord.width = lineWidth; + } + + if (stipplePattern != (short) 0xFFFF) { + if (!lineRecord.isValid() || !lineRecord.stippled) { + GL11.glEnable(GL11.GL_LINE_STIPPLE); + lineRecord.stippled = true; + } + + if (!lineRecord.isValid() || stippleFactor != lineRecord.stippleFactor + || stipplePattern != lineRecord.stipplePattern) { + GL11.glLineStipple(stippleFactor, stipplePattern); + lineRecord.stippleFactor = stippleFactor; + lineRecord.stipplePattern = stipplePattern; + } + } else if (!lineRecord.isValid() || lineRecord.stippled) { + GL11.glDisable(GL11.GL_LINE_STIPPLE); + lineRecord.stippled = false; + } + + if (antialiased) { + if (!lineRecord.isValid() || !lineRecord.smoothed) { + GL11.glEnable(GL11.GL_LINE_SMOOTH); + lineRecord.smoothed = true; + } + if (!lineRecord.isValid() || lineRecord.smoothHint != GL11.GL_NICEST) { + GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST); + lineRecord.smoothHint = GL11.GL_NICEST; + } + } else if (!lineRecord.isValid() || lineRecord.smoothed) { + GL11.glDisable(GL11.GL_LINE_SMOOTH); + lineRecord.smoothed = false; + } + + if (!lineRecord.isValid()) { + lineRecord.validate(); + } + } + + @Override + public void setupPointParameters(final float pointSize, final boolean antialiased, final boolean isSprite, + final boolean useDistanceAttenuation, final FloatBuffer attenuationCoefficients, final float minPointSize, + final float maxPointSize) { + final RenderContext context = ContextManager.getCurrentContext(); + + // TODO: make a record for point states + GL11.glPointSize(pointSize); + + if (isSprite && context.getCapabilities().isPointSpritesSupported()) { + GL11.glEnable(ARBPointSprite.GL_POINT_SPRITE_ARB); + GL11.glTexEnvi(ARBPointSprite.GL_POINT_SPRITE_ARB, ARBPointSprite.GL_COORD_REPLACE_ARB, GL11.GL_TRUE); + } + + if (useDistanceAttenuation && context.getCapabilities().isPointParametersSupported()) { + ARBPointParameters.glPointParameterARB(ARBPointParameters.GL_POINT_DISTANCE_ATTENUATION_ARB, + attenuationCoefficients); + ARBPointParameters.glPointParameterfARB(ARBPointParameters.GL_POINT_SIZE_MIN_ARB, minPointSize); + ARBPointParameters.glPointParameterfARB(ARBPointParameters.GL_POINT_SIZE_MAX_ARB, maxPointSize); + } + + if (antialiased) { + GL11.glEnable(GL11.GL_POINT_SMOOTH); + GL11.glHint(GL11.GL_POINT_SMOOTH_HINT, GL11.GL_NICEST); + } + } + + @Override + public void doApplyState(final RenderState state) { + if (state == null) { + logger.warning("tried to apply a null state."); + return; + } + switch (state.getType()) { + case Texture: + LwjglTextureStateUtil.apply((TextureState) state); + return; + case Light: + LwjglLightStateUtil.apply((LightState) state); + return; + case Blend: + LwjglBlendStateUtil.apply((BlendState) state); + return; + case Clip: + LwjglClipStateUtil.apply((ClipState) state); + return; + case ColorMask: + LwjglColorMaskStateUtil.apply((ColorMaskState) state); + return; + case Cull: + LwjglCullStateUtil.apply((CullState) state); + return; + case Fog: + LwjglFogStateUtil.apply((FogState) state); + return; + case FragmentProgram: + LwjglFragmentProgramStateUtil.apply((FragmentProgramState) state); + return; + case GLSLShader: + LwjglShaderObjectsStateUtil.apply(this, (GLSLShaderObjectsState) state); + return; + case Material: + LwjglMaterialStateUtil.apply((MaterialState) state); + return; + case Offset: + LwjglOffsetStateUtil.apply(this, (OffsetState) state); + return; + case Shading: + LwjglShadingStateUtil.apply((ShadingState) state); + return; + case Stencil: + LwjglStencilStateUtil.apply((StencilState) state); + return; + case VertexProgram: + LwjglVertexProgramStateUtil.apply((VertexProgramState) state); + return; + case Wireframe: + LwjglWireframeStateUtil.apply(this, (WireframeState) state); + return; + case ZBuffer: + LwjglZBufferStateUtil.apply((ZBufferState) state); + return; + } + throw new IllegalArgumentException("Unknown state: " + state); + } + + public void deleteTexture(final Texture texture) { + LwjglTextureStateUtil.deleteTexture(texture); + } + + public void loadTexture(final Texture texture, final int unit) { + LwjglTextureStateUtil.load(texture, unit); + } + + public void deleteTextureIds(final Collection ids) { + LwjglTextureStateUtil.deleteTextureIds(ids); + } + + /** + * Start a new display list. All further renderer commands that can be stored in a display list are part of this new + * list until {@link #endDisplayList()} is called. + * + * @return id of new display list + */ + public int startDisplayList() { + final int id = GL11.glGenLists(1); + + GL11.glNewList(id, GL11.GL_COMPILE); + + return id; + } + + /** + * Ends a display list. Will likely cause an OpenGL exception is a display list is not currently being generated. + */ + public void endDisplayList() { + GL11.glEndList(); + } + + /** + * Draw the given display list. + */ + public void renderDisplayList(final int displayListID) { + GL11.glCallList(displayListID); + } + + public void clearClips() { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord record = context.getRendererRecord(); + record.getScissorClips().clear(); + + LwjglRendererUtil.applyScissors(record); + } + + public void popClip() { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord record = context.getRendererRecord(); + record.getScissorClips().pop(); + + LwjglRendererUtil.applyScissors(record); + } + + public void pushClip(final ReadOnlyRectangle2 rectangle) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord record = context.getRendererRecord(); + record.getScissorClips().push(rectangle); + + LwjglRendererUtil.applyScissors(record); + } + + public void pushEmptyClip() { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord record = context.getRendererRecord(); + record.getScissorClips().push(null); + + LwjglRendererUtil.applyScissors(record); + } + + public void setClipTestEnabled(final boolean enabled) { + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord record = context.getRendererRecord(); + + LwjglRendererUtil.setClippingEnabled(record, enabled); + } + + public void checkAndSetTextureArrayUnit(final int unit, final RendererRecord record, final ContextCapabilities caps) { + if (record.getCurrentTextureArraysUnit() != unit && caps.isMultitextureSupported()) { + ARBMultitexture.glClientActiveTextureARB(ARBMultitexture.GL_TEXTURE0_ARB + unit); + record.setCurrentTextureArraysUnit(unit); + } + } + +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRenderer.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRenderer.java new file mode 100644 index 0000000..5f66a48 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRenderer.java @@ -0,0 +1,566 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.renderer.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Logger; + +import org.lwjgl.opengl.ARBDepthTexture; +import org.lwjgl.opengl.ARBDrawBuffers; +import org.lwjgl.opengl.EXTFramebufferBlit; +import org.lwjgl.opengl.EXTFramebufferMultisample; +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.framework.Scene; +import com.ardor3d.image.Texture; +import com.ardor3d.image.Texture.Type; +import com.ardor3d.image.TextureCubeMap; +import com.ardor3d.image.TextureCubeMap.Face; +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.renderer.AbstractFBOTextureRenderer; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.TextureRendererFactory; +import com.ardor3d.renderer.state.RenderState; +import com.ardor3d.renderer.state.record.RendererRecord; +import com.ardor3d.renderer.state.record.TextureRecord; +import com.ardor3d.renderer.state.record.TextureStateRecord; +import com.ardor3d.scene.state.lwjgl.LwjglTextureStateUtil; +import com.ardor3d.scene.state.lwjgl.util.LwjglTextureUtil; +import com.ardor3d.scenegraph.Spatial; +import com.ardor3d.util.Ardor3dException; +import com.ardor3d.util.TextureKey; +import com.ardor3d.util.geom.BufferUtils; + +/** + *

+ * This class is used by Ardor3D's LWJGL implementation to render textures. Users should not create this class + * directly. + *

+ * + * @see TextureRendererFactory + */ +public class LwjglTextureRenderer extends AbstractFBOTextureRenderer { + private static final Logger logger = Logger.getLogger(LwjglTextureRenderer.class.getName()); + + public LwjglTextureRenderer(final int width, final int height, final int depthBits, final int samples, + final Renderer parentRenderer, final ContextCapabilities caps) { + super(width, height, depthBits, samples, parentRenderer, caps); + + if (caps.getMaxFBOColorAttachments() > 1) { + _attachBuffer = BufferUtils.createIntBuffer(caps.getMaxFBOColorAttachments()); + for (int i = 0; i < caps.getMaxFBOColorAttachments(); i++) { + _attachBuffer.put(EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT + i); + } + } + } + + /** + * setupTexture initializes a new Texture object for use with TextureRenderer. Generates a valid OpenGL + * texture id for this texture and initializes the data type for the texture. + */ + public void setupTexture(final Texture tex) { + if (tex.getType() != Type.TwoDimensional && tex.getType() != Type.CubeMap) { + throw new IllegalArgumentException("Texture type not supported: " + tex.getType()); + } + + final RenderContext context = ContextManager.getCurrentContext(); + final TextureStateRecord record = (TextureStateRecord) context.getStateRecord(RenderState.StateType.Texture); + + // check if we are already setup... if so, throw error. + if (tex.getTextureKey() == null) { + tex.setTextureKey(TextureKey.getRTTKey(tex.getMinificationFilter())); + } else if (tex.getTextureIdForContext(context.getGlContextRep()) != 0) { + throw new Ardor3dException("Texture is already setup and has id."); + } + + // Create the texture + final IntBuffer ibuf = BufferUtils.createIntBuffer(1); + GL11.glGenTextures(ibuf); + final int textureId = ibuf.get(0); + tex.setTextureIdForContext(context.getGlContextRep(), textureId); + + LwjglTextureStateUtil.doTextureBind(tex, 0, true); + + // Initialize our texture with some default data. + final int internalFormat = LwjglTextureUtil.getGLInternalFormat(tex.getTextureStoreFormat()); + final int dataFormat = LwjglTextureUtil.getGLPixelFormatFromStoreFormat(tex.getTextureStoreFormat()); + final int pixelDataType = LwjglTextureUtil.getGLPixelDataType(tex.getRenderedTexturePixelDataType()); + + if (tex.getType() == Type.TwoDimensional) { + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, _width, _height, 0, dataFormat, pixelDataType, + (ByteBuffer) null); + } else { + for (final Face face : Face.values()) { + GL11.glTexImage2D(LwjglTextureStateUtil.getGLCubeMapFace(face), 0, internalFormat, _width, _height, 0, + dataFormat, pixelDataType, (ByteBuffer) null); + } + } + + // Initialize mipmapping for this texture, if requested + if (tex.getMinificationFilter().usesMipMapLevels()) { + EXTFramebufferObject.glGenerateMipmapEXT(LwjglTextureStateUtil.getGLType(tex.getType())); + } + + // Setup filtering and wrap + final TextureRecord texRecord = record.getTextureRecord(textureId, tex.getType()); + LwjglTextureStateUtil.applyFilter(tex, texRecord, 0, record, context.getCapabilities()); + LwjglTextureStateUtil.applyWrap(tex, texRecord, 0, record, context.getCapabilities()); + + logger.fine("setup fbo tex with id " + textureId + ": " + _width + "," + _height); + } + + public void render(final Spatial spat, final List texs, final int clear) { + render(null, spat, null, texs, clear); + } + + public void render(final List spat, final List texs, final int clear) { + render(spat, null, null, texs, clear); + } + + @Override + public void render(final Scene scene, final List texs, final int clear) { + render(null, null, scene, texs, clear); + } + + private void render(final List toDrawA, final Spatial toDrawB, final Scene toDrawC, + final List texs, final int clear) { + + final int maxDrawBuffers = ContextManager.getCurrentContext().getCapabilities().getMaxFBOColorAttachments(); + + // if we only support 1 draw buffer at a time anyway, we'll have to render to each texture individually... + if (maxDrawBuffers == 1 || texs.size() == 1) { + try { + ContextManager.getCurrentContext().pushFBOTextureRenderer(this); + + for (int i = 0; i < texs.size(); i++) { + final Texture tex = texs.get(i); + + setupForSingleTexDraw(tex); + + if (_samples > 0 && _supportsMultisample) { + setMSFBO(); + } + + switchCameraIn(clear); + if (toDrawA != null) { + doDraw(toDrawA); + } else if (toDrawB != null) { + doDraw(toDrawB); + } else { + doDraw(toDrawC); + } + switchCameraOut(); + + if (_samples > 0 && _supportsMultisample) { + blitMSFBO(); + } + + takedownForSingleTexDraw(tex); + } + } finally { + ContextManager.getCurrentContext().popFBOTextureRenderer(); + } + return; + } + try { + ContextManager.getCurrentContext().pushFBOTextureRenderer(this); + + // Otherwise, we can streamline this by rendering to multiple textures at once. + // first determine how many groups we need + final LinkedList depths = new LinkedList(); + final LinkedList colors = new LinkedList(); + for (int i = 0; i < texs.size(); i++) { + final Texture tex = texs.get(i); + if (tex.getTextureStoreFormat().isDepthFormat()) { + depths.add(tex); + } else { + colors.add(tex); + } + } + // we can only render to 1 depth texture at a time, so # groups is at minimum == numDepth + final int groups = Math.max(depths.size(), (int) Math.ceil(colors.size() / (float) maxDrawBuffers)); + + final RenderContext context = ContextManager.getCurrentContext(); + for (int i = 0; i < groups; i++) { + // First handle colors + int colorsAdded = 0; + while (colorsAdded < maxDrawBuffers && !colors.isEmpty()) { + final Texture tex = colors.removeFirst(); + if (tex.getType() == Type.TwoDimensional) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT + colorsAdded, GL11.GL_TEXTURE_2D, + tex.getTextureIdForContext(context.getGlContextRep()), 0); + } else if (tex.getType() == Type.CubeMap) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT + colorsAdded, + LwjglTextureStateUtil.getGLCubeMapFace(((TextureCubeMap) tex).getCurrentRTTFace()), + tex.getTextureIdForContext(context.getGlContextRep()), 0); + } else { + throw new IllegalArgumentException("Invalid texture type: " + tex.getType()); + } + colorsAdded++; + } + + // Now take care of depth. + if (!depths.isEmpty()) { + final Texture tex = depths.removeFirst(); + // Set up our depth texture + if (tex.getType() == Type.TwoDimensional) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, GL11.GL_TEXTURE_2D, + tex.getTextureIdForContext(context.getGlContextRep()), 0); + } else if (tex.getType() == Type.CubeMap) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, + LwjglTextureStateUtil.getGLCubeMapFace(((TextureCubeMap) tex).getCurrentRTTFace()), + tex.getTextureIdForContext(context.getGlContextRep()), 0); + } else { + throw new IllegalArgumentException("Invalid texture type: " + tex.getType()); + } + _usingDepthRB = false; + } else if (!_usingDepthRB) { + // setup our default depth render buffer if not already set + EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, + _depthRBID); + _usingDepthRB = true; + } + + setDrawBuffers(colorsAdded); + setReadBuffer(colorsAdded != 0 ? EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT : GL11.GL_NONE); + + // Check FBO complete + checkFBOComplete(_fboID); + + switchCameraIn(clear); + + if (toDrawA != null) { + doDraw(toDrawA); + } else { + doDraw(toDrawB); + } + + switchCameraOut(); + } + + // automatically generate mipmaps for our textures. + for (int x = 0, max = texs.size(); x < max; x++) { + if (texs.get(x).getMinificationFilter().usesMipMapLevels()) { + final Texture tex = texs.get(x); + if (tex.getMinificationFilter().usesMipMapLevels()) { + LwjglTextureStateUtil.doTextureBind(texs.get(x), 0, true); + EXTFramebufferObject.glGenerateMipmapEXT(LwjglTextureStateUtil.getGLType(tex.getType())); + } + } + } + + } finally { + ContextManager.getCurrentContext().popFBOTextureRenderer(); + } + } + + @Override + protected void setupForSingleTexDraw(final Texture tex) { + final RenderContext context = ContextManager.getCurrentContext(); + final int textureId = tex.getTextureIdForContext(context.getGlContextRep()); + + if (tex.getTextureStoreFormat().isDepthFormat()) { + // No color buffer + EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, 0); + + // Setup depth texture into FBO + if (tex.getType() == Type.TwoDimensional) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, GL11.GL_TEXTURE_2D, textureId, 0); + } else if (tex.getType() == Type.CubeMap) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, + LwjglTextureStateUtil.getGLCubeMapFace(((TextureCubeMap) tex).getCurrentRTTFace()), textureId, + 0); + } else { + throw new IllegalArgumentException("Can not render to texture of type: " + tex.getType()); + } + + setDrawBuffer(GL11.GL_NONE); + setReadBuffer(GL11.GL_NONE); + } else { + // Set color texture into FBO + if (tex.getType() == Type.TwoDimensional) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, GL11.GL_TEXTURE_2D, textureId, 0); + } else if (tex.getType() == Type.CubeMap) { + EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, + LwjglTextureStateUtil.getGLCubeMapFace(((TextureCubeMap) tex).getCurrentRTTFace()), textureId, + 0); + } else { + throw new IllegalArgumentException("Can not render to texture of type: " + tex.getType()); + } + + // setup depth RB + EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, _depthRBID); + + setDrawBuffer(EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT); + setReadBuffer(EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT); + } + + // Check FBO complete + checkFBOComplete(_fboID); + } + + private void setReadBuffer(final int attachVal) { + GL11.glReadBuffer(attachVal); + } + + private void setDrawBuffer(final int attachVal) { + GL11.glDrawBuffer(attachVal); + } + + private void setDrawBuffers(final int maxEntry) { + if (maxEntry <= 1) { + setDrawBuffer(maxEntry != 0 ? EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT : GL11.GL_NONE); + } else { + // We should only get to this point if we support ARBDrawBuffers. + _attachBuffer.clear(); + _attachBuffer.limit(maxEntry); + ARBDrawBuffers.glDrawBuffersARB(_attachBuffer); + } + } + + @Override + protected void takedownForSingleTexDraw(final Texture tex) { + // automatically generate mipmaps for our texture. + if (tex.getMinificationFilter().usesMipMapLevels()) { + LwjglTextureStateUtil.doTextureBind(tex, 0, true); + EXTFramebufferObject.glGenerateMipmapEXT(LwjglTextureStateUtil.getGLType(tex.getType())); + } + } + + @Override + protected void setMSFBO() { + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferBlit.GL_DRAW_FRAMEBUFFER_EXT, _msfboID); + } + + @Override + protected void blitMSFBO() { + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferBlit.GL_READ_FRAMEBUFFER_EXT, _msfboID); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferBlit.GL_DRAW_FRAMEBUFFER_EXT, _fboID); + EXTFramebufferBlit.glBlitFramebufferEXT(0, 0, _width, _height, 0, 0, _width, _height, GL11.GL_COLOR_BUFFER_BIT + | GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); + + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferBlit.GL_READ_FRAMEBUFFER_EXT, 0); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferBlit.GL_DRAW_FRAMEBUFFER_EXT, 0); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0); + } + + /** + * Check the currently bound FBO status for completeness. The passed in fboID is for informational purposes only. + * + * @param fboID + * an id to use for log messages, particularly if there are any issues. + */ + public static void checkFBOComplete(final int fboID) { + final int status = EXTFramebufferObject.glCheckFramebufferStatusEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT); + switch (status) { + case EXTFramebufferObject.GL_FRAMEBUFFER_COMPLETE_EXT: + break; + case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception"); + case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT exception"); + case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT exception"); + case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT exception"); + case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT exception"); + case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT exception"); + case EXTFramebufferObject.GL_FRAMEBUFFER_UNSUPPORTED_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_UNSUPPORTED_EXT exception."); + case EXTFramebufferMultisample.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: + throw new IllegalStateException("FrameBuffer: " + fboID + + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT exception."); + default: + throw new IllegalStateException("Unexpected reply from glCheckFramebufferStatusEXT: " + status); + } + } + + public void copyToTexture(final Texture tex, final int x, final int y, final int width, final int height, + final int xoffset, final int yoffset) { + LwjglTextureStateUtil.doTextureBind(tex, 0, true); + + if (tex.getType() == Type.TwoDimensional) { + GL11.glCopyTexSubImage2D(GL11.GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, width, height); + } else if (tex.getType() == Type.CubeMap) { + GL11.glCopyTexSubImage2D( + LwjglTextureStateUtil.getGLCubeMapFace(((TextureCubeMap) tex).getCurrentRTTFace()), 0, xoffset, + yoffset, x, y, width, height); + } else { + throw new IllegalArgumentException("Invalid texture type: " + tex.getType()); + } + } + + @Override + protected void clearBuffers(final int clear) { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + _parentRenderer.clearBuffers(clear); + } + + @Override + protected void activate() { + // Lazy init + if (_fboID == 0) { + final IntBuffer buffer = BufferUtils.createIntBuffer(1); + + // Create our texture binding FBO + EXTFramebufferObject.glGenFramebuffersEXT(buffer); // generate id + _fboID = buffer.get(0); + + // Create a depth renderbuffer to use for RTT use + EXTFramebufferObject.glGenRenderbuffersEXT(buffer); // generate id + _depthRBID = buffer.get(0); + EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, _depthRBID); + int format = GL11.GL_DEPTH_COMPONENT; + if (_supportsDepthTexture && _depthBits > 0) { + switch (_depthBits) { + case 16: + format = ARBDepthTexture.GL_DEPTH_COMPONENT16_ARB; + break; + case 24: + format = ARBDepthTexture.GL_DEPTH_COMPONENT24_ARB; + break; + case 32: + format = ARBDepthTexture.GL_DEPTH_COMPONENT32_ARB; + break; + default: + // stick with the "undefined" GL_DEPTH_COMPONENT + } + } + EXTFramebufferObject.glRenderbufferStorageEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, format, _width, + _height); + + // unbind... + EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, 0); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0); + + // If we support it, rustle up a multisample framebuffer + renderbuffers + if (_samples != 0 && _supportsMultisample) { + // create ms framebuffer object + EXTFramebufferObject.glGenFramebuffersEXT(buffer); + _msfboID = buffer.get(0); + + // create ms renderbuffers + EXTFramebufferObject.glGenRenderbuffersEXT(buffer); // generate id + _mscolorRBID = buffer.get(0); + EXTFramebufferObject.glGenRenderbuffersEXT(buffer); // generate id + _msdepthRBID = buffer.get(0); + + // set up renderbuffer properties + EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, _mscolorRBID); + EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, + _samples, GL11.GL_RGBA, _width, _height); + + EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, _msdepthRBID); + EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, + _samples, format, _width, _height); + + EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, 0); + + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, _msfboID); + EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, + _mscolorRBID); + EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, + EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, + _msdepthRBID); + + // check for errors + checkFBOComplete(_msfboID); + + // release + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0); + } + + } + + if (_active == 0) { + + final RenderContext context = ContextManager.getCurrentContext(); + final RendererRecord record = context.getRendererRecord(); + + // needed as FBOs do not share this flag it seems + record.setClippingTestValid(false); + + // push a delimiter onto the clip stack + _neededClip = _parentRenderer.isClipTestEnabled(); + if (_neededClip) { + _parentRenderer.pushEmptyClip(); + } + + GL11.glClearColor(_backgroundColor.getRed(), _backgroundColor.getGreen(), _backgroundColor.getBlue(), + _backgroundColor.getAlpha()); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, _fboID); + ContextManager.getCurrentContext().pushEnforcedStates(); + ContextManager.getCurrentContext().clearEnforcedStates(); + ContextManager.getCurrentContext().enforceStates(_enforcedStates); + } + _active++; + } + + @Override + protected void deactivate() { + if (_active == 1) { + final ReadOnlyColorRGBA bgColor = _parentRenderer.getBackgroundColor(); + GL11.glClearColor(bgColor.getRed(), bgColor.getGreen(), bgColor.getBlue(), bgColor.getAlpha()); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0); + + ContextManager.getCurrentContext().popEnforcedStates(); + + if (_neededClip) { + _parentRenderer.popClip(); + } + } + _active--; + } + + public void cleanup() { + if (_fboID != 0) { + final IntBuffer id = BufferUtils.createIntBuffer(1); + id.put(_fboID); + id.rewind(); + EXTFramebufferObject.glDeleteFramebuffersEXT(id); + } + + if (_depthRBID != 0) { + final IntBuffer id = BufferUtils.createIntBuffer(1); + id.put(_depthRBID); + id.rewind(); + EXTFramebufferObject.glDeleteRenderbuffersEXT(id); + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRendererProvider.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRendererProvider.java new file mode 100644 index 0000000..a491fe5 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/renderer/lwjgl/LwjglTextureRendererProvider.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.renderer.lwjgl; + +import java.util.logging.Logger; + +import com.ardor3d.framework.DisplaySettings; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.TextureRenderer; +import com.ardor3d.renderer.TextureRendererProvider; + +public class LwjglTextureRendererProvider implements TextureRendererProvider { + + private static final Logger logger = Logger.getLogger(LwjglTextureRendererProvider.class.getName()); + + public TextureRenderer createTextureRenderer(final int width, final int height, final Renderer renderer, + final ContextCapabilities caps) { + return createTextureRenderer(width, height, 0, 0, renderer, caps); + } + + public TextureRenderer createTextureRenderer(final int width, final int height, final int depthBits, + final int samples, final Renderer renderer, final ContextCapabilities caps) { + return createTextureRenderer(new DisplaySettings(width, height, depthBits, samples), false, renderer, caps); + } + + public TextureRenderer createTextureRenderer(final DisplaySettings settings, final boolean forcePbuffer, + final Renderer renderer, final ContextCapabilities caps) { + if (!forcePbuffer && caps.isFBOSupported()) { + return new LwjglTextureRenderer(settings.getWidth(), settings.getHeight(), settings.getDepthBits(), + settings.getSamples(), renderer, caps); + } else if (caps.isPbufferSupported()) { + return new LwjglPbufferTextureRenderer(settings, renderer, caps); + } else { + logger.severe("No texture renderer support (FBO or Pbuffer)."); + return null; + } + + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglBlendStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglBlendStateUtil.java new file mode 100644 index 0000000..9a65133 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglBlendStateUtil.java @@ -0,0 +1,425 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.ARBImaging; +import org.lwjgl.opengl.ARBMultisample; +import org.lwjgl.opengl.EXTBlendColor; +import org.lwjgl.opengl.EXTBlendEquationSeparate; +import org.lwjgl.opengl.EXTBlendFuncSeparate; +import org.lwjgl.opengl.EXTBlendMinmax; +import org.lwjgl.opengl.EXTBlendSubtract; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.BlendState; +import com.ardor3d.renderer.state.BlendState.BlendEquation; +import com.ardor3d.renderer.state.BlendState.DestinationFunction; +import com.ardor3d.renderer.state.BlendState.SourceFunction; +import com.ardor3d.renderer.state.BlendState.TestFunction; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.BlendStateRecord; + +public abstract class LwjglBlendStateUtil { + + public static void apply(final BlendState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final BlendStateRecord record = (BlendStateRecord) context.getStateRecord(StateType.Blend); + final ContextCapabilities caps = context.getCapabilities(); + context.setCurrentState(StateType.Blend, state); + + if (state.isEnabled()) { + applyBlendEquations(state.isBlendEnabled(), state, record, caps); + applyBlendColor(state.isBlendEnabled(), state, record, caps); + applyBlendFunctions(state.isBlendEnabled(), state, record, caps); + + applyTest(state.isTestEnabled(), state, record); + + if (caps.isMultisampleSupported()) { + applyAlphaCoverage(state.isSampleAlphaToCoverageEnabled(), state.isSampleAlphaToOneEnabled(), record, + caps); + applySampleCoverage(state.isSampleCoverageEnabled(), state, record, caps); + } + } else { + // disable blend + applyBlendEquations(false, state, record, caps); + + // disable alpha test + applyTest(false, state, record); + + // disable sample coverage + if (caps.isMultisampleSupported()) { + applyAlphaCoverage(false, false, record, caps); + applySampleCoverage(false, state, record, caps); + } + } + + if (!record.isValid()) { + record.validate(); + } + } + + protected static void applyBlendEquations(final boolean enabled, final BlendState state, + final BlendStateRecord record, final ContextCapabilities caps) { + if (record.isValid()) { + if (enabled) { + if (!record.blendEnabled) { + GL11.glEnable(GL11.GL_BLEND); + record.blendEnabled = true; + } + final int blendEqRGB = getGLEquationValue(state.getBlendEquationRGB(), caps); + if (caps.isSeparateBlendEquationsSupported()) { + final int blendEqAlpha = getGLEquationValue(state.getBlendEquationAlpha(), caps); + if (record.blendEqRGB != blendEqRGB || record.blendEqAlpha != blendEqAlpha) { + EXTBlendEquationSeparate.glBlendEquationSeparateEXT(blendEqRGB, blendEqAlpha); + record.blendEqRGB = blendEqRGB; + record.blendEqAlpha = blendEqAlpha; + } + } else if (caps.isBlendEquationSupported()) { + if (record.blendEqRGB != blendEqRGB) { + ARBImaging.glBlendEquation(blendEqRGB); + record.blendEqRGB = blendEqRGB; + } + } + } else if (record.blendEnabled) { + GL11.glDisable(GL11.GL_BLEND); + record.blendEnabled = false; + } + + } else { + if (enabled) { + GL11.glEnable(GL11.GL_BLEND); + record.blendEnabled = true; + final int blendEqRGB = getGLEquationValue(state.getBlendEquationRGB(), caps); + if (caps.isSeparateBlendEquationsSupported()) { + final int blendEqAlpha = getGLEquationValue(state.getBlendEquationAlpha(), caps); + EXTBlendEquationSeparate.glBlendEquationSeparateEXT(blendEqRGB, blendEqAlpha); + record.blendEqRGB = blendEqRGB; + record.blendEqAlpha = blendEqAlpha; + } else if (caps.isBlendEquationSupported()) { + ARBImaging.glBlendEquation(blendEqRGB); + record.blendEqRGB = blendEqRGB; + } + } else { + GL11.glDisable(GL11.GL_BLEND); + record.blendEnabled = false; + } + } + } + + protected static void applyBlendColor(final boolean enabled, final BlendState state, final BlendStateRecord record, + final ContextCapabilities caps) { + if (enabled) { + final boolean applyConstant = state.getDestinationFunctionRGB().usesConstantColor() + || state.getSourceFunctionRGB().usesConstantColor() + || (caps.isConstantBlendColorSupported() && (state.getDestinationFunctionAlpha() + .usesConstantColor() || state.getSourceFunctionAlpha().usesConstantColor())); + if (applyConstant && caps.isConstantBlendColorSupported()) { + final ReadOnlyColorRGBA constant = state.getConstantColor(); + if (!record.isValid() || (caps.isConstantBlendColorSupported() && !record.blendColor.equals(constant))) { + ARBImaging.glBlendColor(constant.getRed(), constant.getGreen(), constant.getBlue(), + constant.getAlpha()); + record.blendColor.set(constant); + } + } + } + } + + protected static void applyBlendFunctions(final boolean enabled, final BlendState state, + final BlendStateRecord record, final ContextCapabilities caps) { + if (record.isValid()) { + if (enabled) { + final int glSrcRGB = getGLSrcValue(state.getSourceFunctionRGB(), caps); + final int glDstRGB = getGLDstValue(state.getDestinationFunctionRGB(), caps); + if (caps.isSeparateBlendFunctionsSupported()) { + final int glSrcAlpha = getGLSrcValue(state.getSourceFunctionAlpha(), caps); + final int glDstAlpha = getGLDstValue(state.getDestinationFunctionAlpha(), caps); + if (record.srcFactorRGB != glSrcRGB || record.dstFactorRGB != glDstRGB + || record.srcFactorAlpha != glSrcAlpha || record.dstFactorAlpha != glDstAlpha) { + EXTBlendFuncSeparate.glBlendFuncSeparateEXT(glSrcRGB, glDstRGB, glSrcAlpha, glDstAlpha); + record.srcFactorRGB = glSrcRGB; + record.dstFactorRGB = glDstRGB; + record.srcFactorAlpha = glSrcAlpha; + record.dstFactorAlpha = glDstAlpha; + } + } else if (record.srcFactorRGB != glSrcRGB || record.dstFactorRGB != glDstRGB) { + GL11.glBlendFunc(glSrcRGB, glDstRGB); + record.srcFactorRGB = glSrcRGB; + record.dstFactorRGB = glDstRGB; + } + } + } else { + if (enabled) { + final int glSrcRGB = getGLSrcValue(state.getSourceFunctionRGB(), caps); + final int glDstRGB = getGLDstValue(state.getDestinationFunctionRGB(), caps); + if (caps.isSeparateBlendFunctionsSupported()) { + final int glSrcAlpha = getGLSrcValue(state.getSourceFunctionAlpha(), caps); + final int glDstAlpha = getGLDstValue(state.getDestinationFunctionAlpha(), caps); + EXTBlendFuncSeparate.glBlendFuncSeparateEXT(glSrcRGB, glDstRGB, glSrcAlpha, glDstAlpha); + record.srcFactorRGB = glSrcRGB; + record.dstFactorRGB = glDstRGB; + record.srcFactorAlpha = glSrcAlpha; + record.dstFactorAlpha = glDstAlpha; + } else { + GL11.glBlendFunc(glSrcRGB, glDstRGB); + record.srcFactorRGB = glSrcRGB; + record.dstFactorRGB = glDstRGB; + } + } + } + } + + protected static void applyAlphaCoverage(final boolean sampleAlphaToCoverageEnabled, + final boolean sampleAlphaToOneEnabled, final BlendStateRecord record, final ContextCapabilities caps) { + if (record.isValid()) { + if (sampleAlphaToCoverageEnabled != record.sampleAlphaToCoverageEnabled) { + if (sampleAlphaToCoverageEnabled) { + GL11.glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + } else { + GL11.glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + } + record.sampleAlphaToCoverageEnabled = sampleAlphaToCoverageEnabled; + } + if (sampleAlphaToOneEnabled != record.sampleAlphaToOneEnabled) { + if (sampleAlphaToOneEnabled) { + GL11.glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_ONE_ARB); + } else { + GL11.glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_ONE_ARB); + } + record.sampleAlphaToOneEnabled = sampleAlphaToOneEnabled; + } + } else { + if (sampleAlphaToCoverageEnabled) { + GL11.glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + } else { + GL11.glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + } + record.sampleAlphaToCoverageEnabled = sampleAlphaToCoverageEnabled; + if (sampleAlphaToOneEnabled) { + GL11.glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_ONE_ARB); + } else { + GL11.glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_ONE_ARB); + } + record.sampleAlphaToOneEnabled = sampleAlphaToOneEnabled; + } + } + + protected static void applySampleCoverage(final boolean enabled, final BlendState state, + final BlendStateRecord record, final ContextCapabilities caps) { + + final boolean coverageInverted = state.isSampleCoverageInverted(); + final float coverageValue = state.getSampleCoverage(); + + if (record.isValid()) { + if (enabled) { + if (!record.sampleCoverageEnabled) { + GL11.glEnable(ARBMultisample.GL_SAMPLE_COVERAGE_ARB); + record.sampleCoverageEnabled = true; + } + if (record.sampleCoverageInverted != coverageInverted || record.sampleCoverage != coverageValue) { + ARBMultisample.glSampleCoverageARB(coverageValue, coverageInverted); + record.sampleCoverageInverted = coverageInverted; + record.sampleCoverage = coverageValue; + } + } else { + if (record.sampleCoverageEnabled) { + GL11.glDisable(ARBMultisample.GL_SAMPLE_COVERAGE_ARB); + record.sampleCoverageEnabled = false; + } + } + } else { + if (enabled) { + GL11.glEnable(ARBMultisample.GL_SAMPLE_COVERAGE_ARB); + record.sampleCoverageEnabled = true; + ARBMultisample.glSampleCoverageARB(coverageValue, coverageInverted); + record.sampleCoverageInverted = coverageInverted; + record.sampleCoverage = coverageValue; + } else { + GL11.glDisable(ARBMultisample.GL_SAMPLE_COVERAGE_ARB); + record.sampleCoverageEnabled = false; + } + } + } + + protected static int getGLSrcValue(final SourceFunction function, final ContextCapabilities caps) { + switch (function) { + case Zero: + return GL11.GL_ZERO; + case DestinationColor: + return GL11.GL_DST_COLOR; + case OneMinusDestinationColor: + return GL11.GL_ONE_MINUS_DST_COLOR; + case SourceAlpha: + return GL11.GL_SRC_ALPHA; + case OneMinusSourceAlpha: + return GL11.GL_ONE_MINUS_SRC_ALPHA; + case DestinationAlpha: + return GL11.GL_DST_ALPHA; + case OneMinusDestinationAlpha: + return GL11.GL_ONE_MINUS_DST_ALPHA; + case SourceAlphaSaturate: + return GL11.GL_SRC_ALPHA_SATURATE; + case ConstantColor: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_CONSTANT_COLOR_EXT; + } + // FALLS THROUGH + case OneMinusConstantColor: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_ONE_MINUS_CONSTANT_COLOR_EXT; + } + // FALLS THROUGH + case ConstantAlpha: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_CONSTANT_ALPHA_EXT; + } + // FALLS THROUGH + case OneMinusConstantAlpha: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_ONE_MINUS_CONSTANT_ALPHA_EXT; + } + // FALLS THROUGH + case One: + return GL11.GL_ONE; + } + throw new IllegalArgumentException("Invalid source function type: " + function); + } + + protected static int getGLDstValue(final DestinationFunction function, final ContextCapabilities caps) { + switch (function) { + case Zero: + return GL11.GL_ZERO; + case SourceColor: + return GL11.GL_SRC_COLOR; + case OneMinusSourceColor: + return GL11.GL_ONE_MINUS_SRC_COLOR; + case SourceAlpha: + return GL11.GL_SRC_ALPHA; + case OneMinusSourceAlpha: + return GL11.GL_ONE_MINUS_SRC_ALPHA; + case DestinationAlpha: + return GL11.GL_DST_ALPHA; + case OneMinusDestinationAlpha: + return GL11.GL_ONE_MINUS_DST_ALPHA; + case ConstantColor: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_CONSTANT_COLOR_EXT; + } + // FALLS THROUGH + case OneMinusConstantColor: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_ONE_MINUS_CONSTANT_COLOR_EXT; + } + // FALLS THROUGH + case ConstantAlpha: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_CONSTANT_ALPHA_EXT; + } + // FALLS THROUGH + case OneMinusConstantAlpha: + if (caps.isConstantBlendColorSupported()) { + return EXTBlendColor.GL_ONE_MINUS_CONSTANT_ALPHA_EXT; + } + // FALLS THROUGH + case One: + return GL11.GL_ONE; + } + throw new IllegalArgumentException("Invalid destination function type: " + function); + } + + protected static int getGLEquationValue(final BlendEquation eq, final ContextCapabilities caps) { + switch (eq) { + case Min: + if (caps.isMinMaxBlendEquationsSupported()) { + return EXTBlendMinmax.GL_MIN_EXT; + } + // FALLS THROUGH + case Max: + if (caps.isMinMaxBlendEquationsSupported()) { + return EXTBlendMinmax.GL_MAX_EXT; + } else { + return ARBImaging.GL_FUNC_ADD; + } + case Subtract: + if (caps.isSubtractBlendEquationsSupported()) { + return EXTBlendSubtract.GL_FUNC_SUBTRACT_EXT; + } + // FALLS THROUGH + case ReverseSubtract: + if (caps.isSubtractBlendEquationsSupported()) { + return EXTBlendSubtract.GL_FUNC_REVERSE_SUBTRACT_EXT; + } + // FALLS THROUGH + case Add: + return ARBImaging.GL_FUNC_ADD; + } + throw new IllegalArgumentException("Invalid blend equation: " + eq); + } + + protected static void applyTest(final boolean enabled, final BlendState state, final BlendStateRecord record) { + if (record.isValid()) { + if (enabled) { + if (!record.testEnabled) { + GL11.glEnable(GL11.GL_ALPHA_TEST); + record.testEnabled = true; + } + final int glFunc = getGLFuncValue(state.getTestFunction()); + if (record.alphaFunc != glFunc || record.alphaRef != state.getReference()) { + GL11.glAlphaFunc(glFunc, state.getReference()); + record.alphaFunc = glFunc; + record.alphaRef = state.getReference(); + } + } else if (record.testEnabled) { + GL11.glDisable(GL11.GL_ALPHA_TEST); + record.testEnabled = false; + } + + } else { + if (enabled) { + GL11.glEnable(GL11.GL_ALPHA_TEST); + record.testEnabled = true; + final int glFunc = getGLFuncValue(state.getTestFunction()); + GL11.glAlphaFunc(glFunc, state.getReference()); + record.alphaFunc = glFunc; + record.alphaRef = state.getReference(); + } else { + GL11.glDisable(GL11.GL_ALPHA_TEST); + record.testEnabled = false; + } + } + } + + protected static int getGLFuncValue(final TestFunction function) { + switch (function) { + case Never: + return GL11.GL_NEVER; + case LessThan: + return GL11.GL_LESS; + case EqualTo: + return GL11.GL_EQUAL; + case LessThanOrEqualTo: + return GL11.GL_LEQUAL; + case GreaterThan: + return GL11.GL_GREATER; + case NotEqualTo: + return GL11.GL_NOTEQUAL; + case GreaterThanOrEqualTo: + return GL11.GL_GEQUAL; + case Always: + return GL11.GL_ALWAYS; + } + throw new IllegalArgumentException("Invalid test function type: " + function); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglClipStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglClipStateUtil.java new file mode 100644 index 0000000..d3ee2a0 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglClipStateUtil.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.ClipState; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.ClipStateRecord; + +public abstract class LwjglClipStateUtil { + + public static void apply(final ClipState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final ClipStateRecord record = (ClipStateRecord) context.getStateRecord(StateType.Clip); + context.setCurrentState(StateType.Clip, state); + + final ContextCapabilities caps = context.getCapabilities(); + final int max = Math.min(ClipState.MAX_CLIP_PLANES, caps.getMaxUserClipPlanes()); + + if (state.isEnabled()) { + for (int i = 0; i < max; i++) { + enableClipPlane(i, state.getPlaneEnabled(i), state, record); + } + } else { + for (int i = 0; i < max; i++) { + enableClipPlane(i, false, state, record); + } + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void enableClipPlane(final int planeIndex, final boolean enable, final ClipState state, + final ClipStateRecord record) { + if (enable) { + if (!record.isValid() || !record.planeEnabled[planeIndex]) { + GL11.glEnable(GL11.GL_CLIP_PLANE0 + planeIndex); + record.planeEnabled[planeIndex] = true; + } + + record.buf.rewind(); + record.buf.put(state.getPlaneEquations(planeIndex)); + record.buf.flip(); + GL11.glClipPlane(GL11.GL_CLIP_PLANE0 + planeIndex, record.buf); + + } else { + if (!record.isValid() || record.planeEnabled[planeIndex]) { + GL11.glDisable(GL11.GL_CLIP_PLANE0 + planeIndex); + record.planeEnabled[planeIndex] = false; + } + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglColorMaskStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglColorMaskStateUtil.java new file mode 100644 index 0000000..0a45a97 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglColorMaskStateUtil.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.ColorMaskState; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.ColorMaskStateRecord; + +public abstract class LwjglColorMaskStateUtil { + + public static void apply(final ColorMaskState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final ColorMaskStateRecord record = (ColorMaskStateRecord) context.getStateRecord(StateType.ColorMask); + context.setCurrentState(StateType.ColorMask, state); + + if (state.isEnabled()) { + if (!record.isValid() || !record.is(state.getRed(), state.getGreen(), state.getBlue(), state.getAlpha())) { + GL11.glColorMask(state.getRed(), state.getGreen(), state.getBlue(), state.getAlpha()); + record.set(state.getRed(), state.getGreen(), state.getBlue(), state.getAlpha()); + } + } else if (!record.isValid() || !record.is(true, true, true, true)) { + GL11.glColorMask(true, true, true, true); + record.set(true, true, true, true); + } + + if (!record.isValid()) { + record.validate(); + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglCullStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglCullStateUtil.java new file mode 100644 index 0000000..7a27d18 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglCullStateUtil.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.CullState; +import com.ardor3d.renderer.state.CullState.Face; +import com.ardor3d.renderer.state.CullState.PolygonWind; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.CullStateRecord; + +public abstract class LwjglCullStateUtil { + + public static void apply(final CullState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final CullStateRecord record = (CullStateRecord) context.getStateRecord(StateType.Cull); + context.setCurrentState(StateType.Cull, state); + + if (state.isEnabled()) { + final Face useCullMode = state.getCullFace(); + + switch (useCullMode) { + case Front: + setCull(GL11.GL_FRONT, record); + setCullEnabled(true, record); + break; + case Back: + setCull(GL11.GL_BACK, record); + setCullEnabled(true, record); + break; + case FrontAndBack: + setCull(GL11.GL_FRONT_AND_BACK, record); + setCullEnabled(true, record); + break; + case None: + setCullEnabled(false, record); + break; + } + setGLPolygonWind(state.getPolygonWind(), record); + } else { + setCullEnabled(false, record); + setGLPolygonWind(PolygonWind.CounterClockWise, record); + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void setCullEnabled(final boolean enable, final CullStateRecord record) { + if (!record.isValid() || record.enabled != enable) { + if (enable) { + GL11.glEnable(GL11.GL_CULL_FACE); + } else { + GL11.glDisable(GL11.GL_CULL_FACE); + } + record.enabled = enable; + } + } + + private static void setCull(final int face, final CullStateRecord record) { + if (!record.isValid() || record.face != face) { + GL11.glCullFace(face); + record.face = face; + } + } + + private static void setGLPolygonWind(final PolygonWind windOrder, final CullStateRecord record) { + if (!record.isValid() || record.windOrder != windOrder) { + switch (windOrder) { + case CounterClockWise: + GL11.glFrontFace(GL11.GL_CCW); + break; + case ClockWise: + GL11.glFrontFace(GL11.GL_CW); + break; + } + record.windOrder = windOrder; + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFogStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFogStateUtil.java new file mode 100644 index 0000000..71f4342 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFogStateUtil.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.EXTFogCoord; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.FogState; +import com.ardor3d.renderer.state.FogState.CoordinateSource; +import com.ardor3d.renderer.state.FogState.DensityFunction; +import com.ardor3d.renderer.state.FogState.Quality; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.FogStateRecord; + +public abstract class LwjglFogStateUtil { + + public static void apply(final FogState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final FogStateRecord record = (FogStateRecord) context.getStateRecord(StateType.Fog); + context.setCurrentState(StateType.Fog, state); + + if (state.isEnabled()) { + enableFog(true, record); + + if (record.isValid()) { + if (record.fogStart != state.getStart()) { + GL11.glFogf(GL11.GL_FOG_START, state.getStart()); + record.fogStart = state.getStart(); + } + if (record.fogEnd != state.getEnd()) { + GL11.glFogf(GL11.GL_FOG_END, state.getEnd()); + record.fogEnd = state.getEnd(); + } + if (record.density != state.getDensity()) { + GL11.glFogf(GL11.GL_FOG_DENSITY, state.getDensity()); + record.density = state.getDensity(); + } + } else { + GL11.glFogf(GL11.GL_FOG_START, state.getStart()); + record.fogStart = state.getStart(); + GL11.glFogf(GL11.GL_FOG_END, state.getEnd()); + record.fogEnd = state.getEnd(); + GL11.glFogf(GL11.GL_FOG_DENSITY, state.getDensity()); + record.density = state.getDensity(); + } + + final ReadOnlyColorRGBA fogColor = state.getColor(); + applyFogColor(fogColor, record); + applyFogMode(state.getDensityFunction(), record); + applyFogHint(state.getQuality(), record); + applyFogSource(state.getSource(), record, context.getCapabilities()); + } else { + enableFog(false, record); + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void enableFog(final boolean enable, final FogStateRecord record) { + if (record.isValid()) { + if (enable && !record.enabled) { + GL11.glEnable(GL11.GL_FOG); + record.enabled = true; + } else if (!enable && record.enabled) { + GL11.glDisable(GL11.GL_FOG); + record.enabled = false; + } + } else { + if (enable) { + GL11.glEnable(GL11.GL_FOG); + } else { + GL11.glDisable(GL11.GL_FOG); + } + record.enabled = enable; + } + } + + private static void applyFogColor(final ReadOnlyColorRGBA color, final FogStateRecord record) { + if (!record.isValid() || !color.equals(record.fogColor)) { + record.fogColor.set(color); + record.colorBuff.clear(); + record.colorBuff.put(record.fogColor.getRed()).put(record.fogColor.getGreen()) + .put(record.fogColor.getBlue()).put(record.fogColor.getAlpha()); + record.colorBuff.flip(); + GL11.glFog(GL11.GL_FOG_COLOR, record.colorBuff); + } + } + + private static void applyFogSource(final CoordinateSource source, final FogStateRecord record, + final ContextCapabilities caps) { + if (caps.isFogCoordinatesSupported()) { + if (!record.isValid() || !source.equals(record.source)) { + if (source == CoordinateSource.Depth) { + GL11.glFogi(EXTFogCoord.GL_FOG_COORDINATE_SOURCE_EXT, EXTFogCoord.GL_FRAGMENT_DEPTH_EXT); + } else { + GL11.glFogi(EXTFogCoord.GL_FOG_COORDINATE_SOURCE_EXT, EXTFogCoord.GL_FOG_COORDINATE_EXT); + } + } + } + } + + private static void applyFogMode(final DensityFunction densityFunction, final FogStateRecord record) { + int glMode = 0; + switch (densityFunction) { + case Exponential: + glMode = GL11.GL_EXP; + break; + case Linear: + glMode = GL11.GL_LINEAR; + break; + case ExponentialSquared: + glMode = GL11.GL_EXP2; + break; + } + + if (!record.isValid() || record.fogMode != glMode) { + GL11.glFogi(GL11.GL_FOG_MODE, glMode); + record.fogMode = glMode; + } + } + + private static void applyFogHint(final Quality quality, final FogStateRecord record) { + int glHint = 0; + switch (quality) { + case PerVertex: + glHint = GL11.GL_FASTEST; + break; + case PerPixel: + glHint = GL11.GL_NICEST; + break; + } + + if (!record.isValid() || record.fogHint != glHint) { + GL11.glHint(GL11.GL_FOG_HINT, glHint); + record.fogHint = glHint; + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFragmentProgramStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFragmentProgramStateUtil.java new file mode 100644 index 0000000..fc2ac8f --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglFragmentProgramStateUtil.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.logging.Logger; + +import org.lwjgl.opengl.ARBFragmentProgram; +import org.lwjgl.opengl.ARBProgram; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.FragmentProgramState; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.FragmentProgramStateRecord; +import com.ardor3d.util.geom.BufferUtils; + +public final class LwjglFragmentProgramStateUtil { + private static final Logger logger = Logger.getLogger(LwjglFragmentProgramStateUtil.class.getName()); + + /** + * Queries OpenGL for errors in the fragment program. Errors are logged as SEVERE, noting both the line number and + * message. + */ + private static void checkProgramError() { + if (GL11.glGetError() == GL11.GL_INVALID_OPERATION) { + // retrieve the error position + final IntBuffer errorloc = BufferUtils.createIntBuffer(16); + GL11.glGetInteger(ARBProgram.GL_PROGRAM_ERROR_POSITION_ARB, errorloc); + + logger.severe("Error " + GL11.glGetString(ARBProgram.GL_PROGRAM_ERROR_STRING_ARB) + + " in fragment program on line " + errorloc.get(0)); + } + } + + private static int create(final ByteBuffer program) { + + final IntBuffer buf = BufferUtils.createIntBuffer(1); + + ARBProgram.glGenProgramsARB(buf); + ARBProgram.glBindProgramARB(ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB, buf.get(0)); + ARBProgram.glProgramStringARB(ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB, + ARBProgram.GL_PROGRAM_FORMAT_ASCII_ARB, program); + + checkProgramError(); + + return buf.get(0); + } + + public static void apply(final FragmentProgramState state) { + final RenderContext context = ContextManager.getCurrentContext(); + if (context.getCapabilities().isFragmentProgramSupported()) { + final FragmentProgramStateRecord record = (FragmentProgramStateRecord) context + .getStateRecord(StateType.FragmentProgram); + context.setCurrentState(StateType.FragmentProgram, state); + + if (!record.isValid() || record.getReference() != state) { + record.setReference(state); + if (state.isEnabled()) { + // Fragment program not yet loaded + if (state._getProgramID() == -1) { + if (state.getProgramAsBuffer() != null) { + final int id = create(state.getProgramAsBuffer()); + state._setProgramID(id); + } else { + return; + } + } + + GL11.glEnable(ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB); + ARBProgram.glBindProgramARB(ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB, state._getProgramID()); + + // load environmental parameters... + // TODO: Reevaluate how this is done. + /* + * for (int i = 0; i < envparameters.length; i++) if (envparameters[i] != null) + * ARBFragmentProgram.glProgramEnvParameter4fARB( ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB, i, + * envparameters[i][0], envparameters[i][1], envparameters[i][2], envparameters[i][3]); + */ + + // load local parameters... + if (state.isUsingParameters()) { + // no parameters are used + for (int i = 0; i < state._getParameters().length; i++) { + if (state._getParameters()[i] != null) { + ARBProgram.glProgramLocalParameter4fARB(ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB, i, + state._getParameters()[i][0], state._getParameters()[i][1], + state._getParameters()[i][2], state._getParameters()[i][3]); + } + } + } + + } else { + GL11.glDisable(ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB); + } + } + + if (!record.isValid()) { + record.validate(); + } + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglLightStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglLightStateUtil.java new file mode 100644 index 0000000..0ef2391 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglLightStateUtil.java @@ -0,0 +1,372 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import com.ardor3d.light.DirectionalLight; +import com.ardor3d.light.Light; +import com.ardor3d.light.PointLight; +import com.ardor3d.light.SpotLight; +import com.ardor3d.math.ColorRGBA; +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.math.type.ReadOnlyMatrix4; +import com.ardor3d.math.type.ReadOnlyVector3; +import com.ardor3d.renderer.Camera; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.LightState; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.LightRecord; +import com.ardor3d.renderer.state.record.LightStateRecord; + +public abstract class LwjglLightStateUtil { + + public static void apply(final LightState state) { + final RenderContext context = ContextManager.getCurrentContext(); + final LightStateRecord record = (LightStateRecord) context.getStateRecord(StateType.Light); + context.setCurrentState(StateType.Light, state); + + if (state.isEnabled() && LightState.LIGHTS_ENABLED) { + setLightEnabled(true, record); + setTwoSided(state.getTwoSidedLighting(), record); + setLocalViewer(state.getLocalViewer(), record); + if (context.getCapabilities().isOpenGL1_2Supported()) { + setSpecularControl(state.getSeparateSpecular(), record); + } + + for (int i = 0, max = state.getNumberOfChildren(); i < max; i++) { + final Light light = state.get(i); + LightRecord lr = record.getLightRecord(i); + // TODO: use the reference to get the lightrecord - rherlitz + + if (lr == null) { + lr = new LightRecord(); + record.setLightRecord(lr, i); + } + + if (light == null) { + setSingleLightEnabled(false, i, record, lr); + } else { + if (light.isEnabled()) { + setLight(i, light, state, record, lr); + } else { + setSingleLightEnabled(false, i, record, lr); + } + } + } + + // disable lights at and above the max count in this state + for (int i = state.getNumberOfChildren(); i < LightState.MAX_LIGHTS_ALLOWED; i++) { + LightRecord lr = record.getLightRecord(i); + + if (lr == null) { + lr = new LightRecord(); + record.setLightRecord(lr, i); + } + setSingleLightEnabled(false, i, record, lr); + } + + if ((state.getLightMask() & LightState.MASK_GLOBALAMBIENT) == 0) { + setModelAmbient(record, state.getGlobalAmbient()); + } else { + setModelAmbient(record, ColorRGBA.BLACK_NO_ALPHA); + } + } else { + setLightEnabled(false, record); + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void setLight(final int index, final Light light, final LightState state, + final LightStateRecord record, final LightRecord lr) { + setSingleLightEnabled(true, index, record, lr); + + if ((state.getLightMask() & LightState.MASK_AMBIENT) == 0 + && (light.getLightMask() & LightState.MASK_AMBIENT) == 0) { + setAmbient(index, record, light.getAmbient(), lr); + } else { + setAmbient(index, record, ColorRGBA.BLACK_NO_ALPHA, lr); + } + + if ((state.getLightMask() & LightState.MASK_DIFFUSE) == 0 + && (light.getLightMask() & LightState.MASK_DIFFUSE) == 0) { + setDiffuse(index, record, light.getDiffuse(), lr); + } else { + setDiffuse(index, record, ColorRGBA.BLACK_NO_ALPHA, lr); + } + + if ((state.getLightMask() & LightState.MASK_SPECULAR) == 0 + && (light.getLightMask() & LightState.MASK_SPECULAR) == 0) { + setSpecular(index, record, light.getSpecular(), lr); + } else { + setSpecular(index, record, ColorRGBA.BLACK_NO_ALPHA, lr); + } + + if (light.isAttenuate()) { + setAttenuate(true, index, light, record, lr); + + } else { + setAttenuate(false, index, light, record, lr); + + } + + switch (light.getType()) { + case Directional: { + final DirectionalLight dirLight = (DirectionalLight) light; + + final ReadOnlyVector3 direction = dirLight.getDirection(); + setPosition(index, record, -direction.getXf(), -direction.getYf(), -direction.getZf(), 0, lr); + break; + } + case Point: + case Spot: { + final PointLight pointLight = (PointLight) light; + final ReadOnlyVector3 location = pointLight.getLocation(); + setPosition(index, record, location.getXf(), location.getYf(), location.getZf(), 1, lr); + break; + } + } + + if (light.getType() == Light.Type.Spot) { + final SpotLight spot = (SpotLight) light; + setSpotCutoff(index, record, spot.getAngle(), lr); + final ReadOnlyVector3 direction = spot.getDirection(); + setSpotDirection(index, record, direction.getXf(), direction.getYf(), direction.getZf(), 0); + setSpotExponent(index, record, spot.getExponent(), lr); + } else { + // set the cutoff to 180, which causes the other spot params to be + // ignored. + setSpotCutoff(index, record, 180, lr); + } + } + + private static void setSingleLightEnabled(final boolean enable, final int index, final LightStateRecord record, + final LightRecord lr) { + if (!record.isValid() || lr.isEnabled() != enable) { + if (enable) { + GL11.glEnable(GL11.GL_LIGHT0 + index); + } else { + GL11.glDisable(GL11.GL_LIGHT0 + index); + } + + lr.setEnabled(enable); + } + } + + private static void setLightEnabled(final boolean enable, final LightStateRecord record) { + if (!record.isValid() || record.isEnabled() != enable) { + if (enable) { + GL11.glEnable(GL11.GL_LIGHTING); + } else { + GL11.glDisable(GL11.GL_LIGHTING); + } + record.setEnabled(enable); + } + } + + private static void setTwoSided(final boolean twoSided, final LightStateRecord record) { + if (!record.isValid() || record.isTwoSidedOn() != twoSided) { + if (twoSided) { + GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE); + } else { + GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_FALSE); + } + record.setTwoSidedOn(twoSided); + } + } + + private static void setLocalViewer(final boolean localViewer, final LightStateRecord record) { + if (!record.isValid() || record.isLocalViewer() != localViewer) { + if (localViewer) { + GL11.glLightModeli(GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_TRUE); + } else { + GL11.glLightModeli(GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_FALSE); + } + record.setLocalViewer(localViewer); + } + } + + private static void setSpecularControl(final boolean separateSpecularOn, final LightStateRecord record) { + if (!record.isValid() || record.isSeparateSpecular() != separateSpecularOn) { + if (separateSpecularOn) { + GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR); + } else { + GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SINGLE_COLOR); + } + record.setSeparateSpecular(separateSpecularOn); + } + } + + private static void setModelAmbient(final LightStateRecord record, final ReadOnlyColorRGBA color) { + if (!record.isValid() || !record.globalAmbient.equals(color)) { + record.lightBuffer.clear(); + record.lightBuffer.put(color.getRed()); + record.lightBuffer.put(color.getGreen()); + record.lightBuffer.put(color.getBlue()); + record.lightBuffer.put(color.getAlpha()); + record.lightBuffer.flip(); + GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, record.lightBuffer); + record.globalAmbient.set(color); + } + } + + private static void setAmbient(final int index, final LightStateRecord record, final ReadOnlyColorRGBA ambient, + final LightRecord lr) { + if (!record.isValid() || !lr.ambient.equals(ambient)) { + record.lightBuffer.clear(); + record.lightBuffer.put(ambient.getRed()); + record.lightBuffer.put(ambient.getGreen()); + record.lightBuffer.put(ambient.getBlue()); + record.lightBuffer.put(ambient.getAlpha()); + record.lightBuffer.flip(); + GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_AMBIENT, record.lightBuffer); + lr.ambient.set(ambient); + } + } + + private static void setDiffuse(final int index, final LightStateRecord record, final ReadOnlyColorRGBA diffuse, + final LightRecord lr) { + if (!record.isValid() || !lr.diffuse.equals(diffuse)) { + record.lightBuffer.clear(); + record.lightBuffer.put(diffuse.getRed()); + record.lightBuffer.put(diffuse.getGreen()); + record.lightBuffer.put(diffuse.getBlue()); + record.lightBuffer.put(diffuse.getAlpha()); + record.lightBuffer.flip(); + GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_DIFFUSE, record.lightBuffer); + lr.diffuse.set(diffuse); + } + } + + private static void setSpecular(final int index, final LightStateRecord record, final ReadOnlyColorRGBA specular, + final LightRecord lr) { + if (!record.isValid() || !lr.specular.equals(specular)) { + record.lightBuffer.clear(); + record.lightBuffer.put(specular.getRed()); + record.lightBuffer.put(specular.getGreen()); + record.lightBuffer.put(specular.getBlue()); + record.lightBuffer.put(specular.getAlpha()); + record.lightBuffer.flip(); + GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_SPECULAR, record.lightBuffer); + lr.specular.set(specular); + } + } + + private static void setPosition(final int index, final LightStateRecord record, final float positionX, + final float positionY, final float positionZ, final float positionW, final LightRecord lr) { + // From OpenGL Docs: + // The light position is transformed by the contents of the current top + // of the ModelView matrix stack when you specify the light position + // with a call to glLightfv(GL_LIGHT_POSITION,...). If you later change + // the ModelView matrix, such as when the view changes for the next + // frame, the light position isn't automatically retransformed by the + // new contents of the ModelView matrix. If you want to update the + // light's position, you must again specify the light position with a + // call to glLightfv(GL_LIGHT_POSITION,...). + + // XXX: This is a hack until we get a better lighting model up + final ReadOnlyMatrix4 modelViewMatrix = Camera.getCurrentCamera().getModelViewMatrix(); + + if (!record.isValid() || lr.position.getXf() != positionX || lr.position.getYf() != positionY + || lr.position.getZf() != positionZ || lr.position.getWf() != positionW + || !lr.modelViewMatrix.equals(modelViewMatrix)) { + + record.lightBuffer.clear(); + record.lightBuffer.put(positionX); + record.lightBuffer.put(positionY); + record.lightBuffer.put(positionZ); + record.lightBuffer.put(positionW); + record.lightBuffer.flip(); + GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_POSITION, record.lightBuffer); + + lr.position.set(positionX, positionY, positionZ, positionW); + if (!Camera.getCurrentCamera().isFrameDirty()) { + lr.modelViewMatrix.set(modelViewMatrix); + } + } + } + + private static void setSpotDirection(final int index, final LightStateRecord record, final float directionX, + final float directionY, final float directionZ, final float value) { + // From OpenGL Docs: + // The light position is transformed by the contents of the current top + // of the ModelView matrix stack when you specify the light position + // with a call to glLightfv(GL_LIGHT_POSITION,...). If you later change + // the ModelView matrix, such as when the view changes for the next + // frame, the light position isn't automatically retransformed by the + // new contents of the ModelView matrix. If you want to update the + // light's position, you must again specify the light position with a + // call to glLightfv(GL_LIGHT_POSITION,...). + record.lightBuffer.clear(); + record.lightBuffer.put(directionX); + record.lightBuffer.put(directionY); + record.lightBuffer.put(directionZ); + record.lightBuffer.put(value); + record.lightBuffer.flip(); + GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_SPOT_DIRECTION, record.lightBuffer); + } + + private static void setConstant(final int index, final float constant, final LightRecord lr, final boolean force) { + if (force || constant != lr.getConstant()) { + GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_CONSTANT_ATTENUATION, constant); + lr.setConstant(constant); + } + } + + private static void setLinear(final int index, final float linear, final LightRecord lr, final boolean force) { + if (force || linear != lr.getLinear()) { + GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_LINEAR_ATTENUATION, linear); + lr.setLinear(linear); + } + } + + private static void setQuadratic(final int index, final float quad, final LightRecord lr, final boolean force) { + if (force || quad != lr.getQuadratic()) { + GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_QUADRATIC_ATTENUATION, quad); + lr.setQuadratic(quad); + } + } + + private static void setAttenuate(final boolean attenuate, final int index, final Light light, + final LightStateRecord record, final LightRecord lr) { + if (attenuate) { + setConstant(index, light.getConstant(), lr, !record.isValid()); + setLinear(index, light.getLinear(), lr, !record.isValid()); + setQuadratic(index, light.getQuadratic(), lr, !record.isValid()); + } else { + setConstant(index, 1, lr, !record.isValid()); + setLinear(index, 0, lr, !record.isValid()); + setQuadratic(index, 0, lr, !record.isValid()); + } + lr.setAttenuate(attenuate); + } + + private static void setSpotExponent(final int index, final LightStateRecord record, final float exponent, + final LightRecord lr) { + if (!record.isValid() || lr.getSpotExponent() != exponent) { + GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_SPOT_EXPONENT, exponent); + lr.setSpotExponent(exponent); + } + } + + private static void setSpotCutoff(final int index, final LightStateRecord record, final float cutoff, + final LightRecord lr) { + if (!record.isValid() || lr.getSpotCutoff() != cutoff) { + GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_SPOT_CUTOFF, cutoff); + lr.setSpotCutoff(cutoff); + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglMaterialStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglMaterialStateUtil.java new file mode 100644 index 0000000..287624e --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglMaterialStateUtil.java @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.MaterialState; +import com.ardor3d.renderer.state.MaterialState.ColorMaterial; +import com.ardor3d.renderer.state.MaterialState.MaterialFace; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.MaterialStateRecord; + +public abstract class LwjglMaterialStateUtil { + + public static void apply(final MaterialState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final MaterialStateRecord record = (MaterialStateRecord) context.getStateRecord(StateType.Material); + context.setCurrentState(StateType.Material, state); + + if (state.isEnabled()) { + // setup colormaterial, if changed. + applyColorMaterial(state.getColorMaterial(), state.getColorMaterialFace(), record); + + // apply colors, if needed and not what is currently set. + applyColor(ColorMaterial.Ambient, state.getAmbient(), state.getBackAmbient(), record); + applyColor(ColorMaterial.Diffuse, state.getDiffuse(), state.getBackDiffuse(), record); + applyColor(ColorMaterial.Emissive, state.getEmissive(), state.getBackEmissive(), record); + applyColor(ColorMaterial.Specular, state.getSpecular(), state.getBackSpecular(), record); + + // set our shine + applyShininess(state.getShininess(), state.getBackShininess(), record); + } else { + // apply defaults + applyColorMaterial(MaterialState.DEFAULT_COLOR_MATERIAL, MaterialState.DEFAULT_COLOR_MATERIAL_FACE, record); + + applyColor(ColorMaterial.Ambient, MaterialState.DEFAULT_AMBIENT, MaterialState.DEFAULT_AMBIENT, record); + applyColor(ColorMaterial.Diffuse, MaterialState.DEFAULT_DIFFUSE, MaterialState.DEFAULT_DIFFUSE, record); + applyColor(ColorMaterial.Emissive, MaterialState.DEFAULT_EMISSIVE, MaterialState.DEFAULT_EMISSIVE, record); + applyColor(ColorMaterial.Specular, MaterialState.DEFAULT_SPECULAR, MaterialState.DEFAULT_SPECULAR, record); + + applyShininess(MaterialState.DEFAULT_SHININESS, MaterialState.DEFAULT_SHININESS, record); + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void applyColor(final ColorMaterial glMatColor, final ReadOnlyColorRGBA frontColor, + final ReadOnlyColorRGBA backColor, final MaterialStateRecord record) { + final int glMat = getGLColorMaterial(glMatColor); + if (frontColor.equals(backColor)) { + // consolidate to one call + if (!isVertexProvidedColor(MaterialFace.FrontAndBack, glMatColor, record)) { + if (!record.isValid() || !record.isSetColor(MaterialFace.FrontAndBack, glMatColor, frontColor, record)) { + record.tempColorBuff.clear(); + record.tempColorBuff.put(frontColor.getRed()).put(frontColor.getGreen()).put(frontColor.getBlue()) + .put(frontColor.getAlpha()); + record.tempColorBuff.flip(); + GL11.glMaterial(getGLMaterialFace(MaterialFace.FrontAndBack), glMat, record.tempColorBuff); + record.setColor(MaterialFace.FrontAndBack, glMatColor, frontColor); + } + } + } else { + if (!isVertexProvidedColor(MaterialFace.Front, glMatColor, record)) { + if (!record.isValid() || !record.isSetColor(MaterialFace.Front, glMatColor, frontColor, record)) { + record.tempColorBuff.clear(); + record.tempColorBuff.put(frontColor.getRed()).put(frontColor.getGreen()).put(frontColor.getBlue()) + .put(frontColor.getAlpha()); + record.tempColorBuff.flip(); + GL11.glMaterial(getGLMaterialFace(MaterialFace.Front), glMat, record.tempColorBuff); + record.setColor(MaterialFace.Front, glMatColor, frontColor); + } + } + + if (!isVertexProvidedColor(MaterialFace.Back, glMatColor, record)) { + if (!record.isValid() || !record.isSetColor(MaterialFace.Back, glMatColor, backColor, record)) { + record.tempColorBuff.clear(); + record.tempColorBuff.put(backColor.getRed()).put(backColor.getGreen()).put(backColor.getBlue()) + .put(backColor.getAlpha()); + record.tempColorBuff.flip(); + GL11.glMaterial(getGLMaterialFace(MaterialFace.Back), glMat, record.tempColorBuff); + record.setColor(MaterialFace.Back, glMatColor, backColor); + } + } + } + } + + private static boolean isVertexProvidedColor(final MaterialFace face, final ColorMaterial glMatColor, + final MaterialStateRecord record) { + if (face != record.colorMaterialFace) { + return false; + } + switch (glMatColor) { + case Ambient: + return record.colorMaterial == ColorMaterial.Ambient + || record.colorMaterial == ColorMaterial.AmbientAndDiffuse; + case Diffuse: + return record.colorMaterial == ColorMaterial.Diffuse + || record.colorMaterial == ColorMaterial.AmbientAndDiffuse; + case Specular: + return record.colorMaterial == ColorMaterial.Specular; + case Emissive: + return record.colorMaterial == ColorMaterial.Emissive; + } + return false; + } + + private static void applyColorMaterial(final ColorMaterial colorMaterial, final MaterialFace face, + final MaterialStateRecord record) { + if (!record.isValid() || face != record.colorMaterialFace || colorMaterial != record.colorMaterial) { + if (colorMaterial == ColorMaterial.None) { + GL11.glDisable(GL11.GL_COLOR_MATERIAL); + } else { + final int glMat = getGLColorMaterial(colorMaterial); + final int glFace = getGLMaterialFace(face); + + GL11.glColorMaterial(glFace, glMat); + GL11.glEnable(GL11.GL_COLOR_MATERIAL); + record.resetColorsForCM(face, colorMaterial); + } + record.colorMaterial = colorMaterial; + record.colorMaterialFace = face; + } + } + + private static void applyShininess(final float frontShininess, final float backShininess, + final MaterialStateRecord record) { + if (frontShininess == backShininess) { + // consolidate to one call + if (!record.isValid() || frontShininess != record.frontShininess || record.backShininess != backShininess) { + GL11.glMaterialf(getGLMaterialFace(MaterialFace.FrontAndBack), GL11.GL_SHININESS, frontShininess); + record.backShininess = record.frontShininess = frontShininess; + } + } else { + if (!record.isValid() || frontShininess != record.frontShininess) { + GL11.glMaterialf(getGLMaterialFace(MaterialFace.Front), GL11.GL_SHININESS, frontShininess); + record.frontShininess = frontShininess; + } + + if (!record.isValid() || backShininess != record.backShininess) { + GL11.glMaterialf(getGLMaterialFace(MaterialFace.Back), GL11.GL_SHININESS, backShininess); + record.backShininess = backShininess; + } + } + } + + /** + * Converts the color material setting of this state to a GL constant. + * + * @return the GL constant + */ + private static int getGLColorMaterial(final ColorMaterial material) { + switch (material) { + case None: + return GL11.GL_NONE; + case Ambient: + return GL11.GL_AMBIENT; + case Diffuse: + return GL11.GL_DIFFUSE; + case AmbientAndDiffuse: + return GL11.GL_AMBIENT_AND_DIFFUSE; + case Emissive: + return GL11.GL_EMISSION; + case Specular: + return GL11.GL_SPECULAR; + } + throw new IllegalArgumentException("invalid color material setting: " + material); + } + + /** + * Converts the material face setting of this state to a GL constant. + * + * @return the GL constant + */ + private static int getGLMaterialFace(final MaterialFace face) { + switch (face) { + case Front: + return GL11.GL_FRONT; + case Back: + return GL11.GL_BACK; + case FrontAndBack: + return GL11.GL_FRONT_AND_BACK; + } + throw new IllegalArgumentException("invalid material face setting: " + face); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglOffsetStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglOffsetStateUtil.java new file mode 100644 index 0000000..2737373 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglOffsetStateUtil.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.lwjgl.LwjglRenderer; +import com.ardor3d.renderer.state.OffsetState; +import com.ardor3d.renderer.state.OffsetState.OffsetType; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.OffsetStateRecord; + +public abstract class LwjglOffsetStateUtil { + + public static void apply(final LwjglRenderer renderer, final OffsetState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final OffsetStateRecord record = (OffsetStateRecord) context.getStateRecord(StateType.Offset); + context.setCurrentState(StateType.Offset, state); + + if (state.isEnabled()) { + // enable any set offset types + setOffsetEnabled(OffsetType.Fill, state.isTypeEnabled(OffsetType.Fill), record); + setOffsetEnabled(OffsetType.Line, state.isTypeEnabled(OffsetType.Line), record); + setOffsetEnabled(OffsetType.Point, state.isTypeEnabled(OffsetType.Point), record); + + // set factor and units. + setOffset(state.getFactor(), state.getUnits(), record); + } else { + // disable all offset types + setOffsetEnabled(OffsetType.Fill, false, record); + setOffsetEnabled(OffsetType.Line, false, record); + setOffsetEnabled(OffsetType.Point, false, record); + + // set factor and units to default 0, 0. + setOffset(0, 0, record); + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void setOffsetEnabled(final OffsetType type, final boolean typeEnabled, + final OffsetStateRecord record) { + final int glType = getGLType(type); + if (!record.isValid() || typeEnabled != record.enabledOffsets.contains(type)) { + if (typeEnabled) { + GL11.glEnable(glType); + } else { + GL11.glDisable(glType); + } + } + } + + private static void setOffset(final float factor, final float units, final OffsetStateRecord record) { + if (!record.isValid() || record.factor != factor || record.units != units) { + GL11.glPolygonOffset(factor, units); + record.factor = factor; + record.units = units; + } + } + + private static int getGLType(final OffsetType type) { + switch (type) { + case Fill: + return GL11.GL_POLYGON_OFFSET_FILL; + case Line: + return GL11.GL_POLYGON_OFFSET_LINE; + case Point: + return GL11.GL_POLYGON_OFFSET_POINT; + } + throw new IllegalArgumentException("invalid type: " + type); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShaderObjectsStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShaderObjectsStateUtil.java new file mode 100644 index 0000000..1440cd1 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShaderObjectsStateUtil.java @@ -0,0 +1,361 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.logging.Logger; + +import org.lwjgl.opengl.ARBFragmentShader; +import org.lwjgl.opengl.ARBGeometryShader4; +import org.lwjgl.opengl.ARBShaderObjects; +import org.lwjgl.opengl.ARBTessellationShader; +import org.lwjgl.opengl.ARBVertexProgram; +import org.lwjgl.opengl.ARBVertexShader; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20; + +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.lwjgl.LwjglRenderer; +import com.ardor3d.renderer.state.GLSLShaderObjectsState; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.ShaderObjectsStateRecord; +import com.ardor3d.scene.state.lwjgl.shader.LwjglShaderUtil; +import com.ardor3d.util.Ardor3dException; +import com.ardor3d.util.geom.BufferUtils; +import com.ardor3d.util.shader.ShaderVariable; + +public abstract class LwjglShaderObjectsStateUtil { + private static final Logger logger = Logger.getLogger(LwjglShaderObjectsStateUtil.class.getName()); + + protected static void sendToGL(final GLSLShaderObjectsState state, final ContextCapabilities caps) { + if (state.getVertexShader() == null && state.getFragmentShader() == null) { + logger.warning("Could not find shader resources!" + "(both inputbuffers are null)"); + state._needSendShader = false; + return; + } + + if (state._programID == -1) { + state._programID = ARBShaderObjects.glCreateProgramObjectARB(); + } + + if (state.getVertexShader() != null) { + if (state._vertexShaderID != -1) { + removeVertShader(state); + } + + state._vertexShaderID = ARBShaderObjects.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB); + + // Create the sources + ARBShaderObjects.glShaderSourceARB(state._vertexShaderID, state.getVertexShader()); + + // Compile the vertex shader + final IntBuffer compiled = BufferUtils.createIntBuffer(1); + ARBShaderObjects.glCompileShaderARB(state._vertexShaderID); + ARBShaderObjects.glGetObjectParameterARB(state._vertexShaderID, + ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); + checkProgramError(compiled, state._vertexShaderID, state._vertexShaderName); + + // Attach the program + ARBShaderObjects.glAttachObjectARB(state._programID, state._vertexShaderID); + } else if (state._vertexShaderID != -1) { + removeVertShader(state); + state._vertexShaderID = -1; + } + + if (state.getFragmentShader() != null) { + if (state._fragmentShaderID != -1) { + removeFragShader(state); + } + + state._fragmentShaderID = ARBShaderObjects + .glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB); + + // Create the sources + ARBShaderObjects.glShaderSourceARB(state._fragmentShaderID, state.getFragmentShader()); + + // Compile the fragment shader + final IntBuffer compiled = BufferUtils.createIntBuffer(1); + ARBShaderObjects.glCompileShaderARB(state._fragmentShaderID); + ARBShaderObjects.glGetObjectParameterARB(state._fragmentShaderID, + ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); + checkProgramError(compiled, state._fragmentShaderID, state._fragmentShaderName); + + // Attach the program + ARBShaderObjects.glAttachObjectARB(state._programID, state._fragmentShaderID); + } else if (state._fragmentShaderID != -1) { + removeFragShader(state); + state._fragmentShaderID = -1; + } + + if (caps.isGeometryShader4Supported()) { + if (state.getGeometryShader() != null) { + if (state._geometryShaderID != -1) { + removeGeomShader(state); + } + + state._geometryShaderID = ARBShaderObjects + .glCreateShaderObjectARB(ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB); + + // Create the sources + ARBShaderObjects.glShaderSourceARB(state._geometryShaderID, state.getGeometryShader()); + + // Compile the fragment shader + final IntBuffer compiled = BufferUtils.createIntBuffer(1); + ARBShaderObjects.glCompileShaderARB(state._geometryShaderID); + ARBShaderObjects.glGetObjectParameterARB(state._geometryShaderID, + ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); + checkProgramError(compiled, state._geometryShaderID, state._geometryShaderName); + + // Attach the program + ARBShaderObjects.glAttachObjectARB(state._programID, state._geometryShaderID); + } else if (state._geometryShaderID != -1) { + removeGeomShader(state); + state._geometryShaderID = -1; + } + } + + if (caps.isTessellationShadersSupported()) { + if (state.getTessellationControlShader() != null) { + if (state._tessellationControlShaderID != -1) { + removeTessControlShader(state); + } + + state._tessellationControlShaderID = ARBShaderObjects + .glCreateShaderObjectARB(ARBTessellationShader.GL_TESS_CONTROL_SHADER); + + // Create the sources + ARBShaderObjects.glShaderSourceARB(state._tessellationControlShaderID, + state.getTessellationControlShader()); + + // Compile the tessellation control shader + final IntBuffer compiled = BufferUtils.createIntBuffer(1); + ARBShaderObjects.glCompileShaderARB(state._tessellationControlShaderID); + ARBShaderObjects.glGetObjectParameterARB(state._tessellationControlShaderID, + ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); + checkProgramError(compiled, state._tessellationControlShaderID, state._tessellationControlShaderName); + + // Attach the program + ARBShaderObjects.glAttachObjectARB(state._programID, state._tessellationControlShaderID); + } else if (state._tessellationControlShaderID != -1) { + removeTessControlShader(state); + state._tessellationControlShaderID = -1; + } + + if (state.getTessellationEvaluationShader() != null) { + if (state._tessellationEvaluationShaderID != -1) { + removeTessEvalShader(state); + } + + state._tessellationEvaluationShaderID = ARBShaderObjects + .glCreateShaderObjectARB(ARBTessellationShader.GL_TESS_CONTROL_SHADER); + + // Create the sources + ARBShaderObjects.glShaderSourceARB(state._tessellationEvaluationShaderID, + state.getTessellationEvaluationShader()); + + // Compile the tessellation control shader + final IntBuffer compiled = BufferUtils.createIntBuffer(1); + ARBShaderObjects.glCompileShaderARB(state._tessellationEvaluationShaderID); + ARBShaderObjects.glGetObjectParameterARB(state._tessellationEvaluationShaderID, + ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); + checkProgramError(compiled, state._tessellationEvaluationShaderID, + state._tessellationEvaluationShaderName); + + // Attach the program + ARBShaderObjects.glAttachObjectARB(state._programID, state._tessellationEvaluationShaderID); + } else if (state._tessellationEvaluationShaderID != -1) { + removeTessEvalShader(state); + state._tessellationEvaluationShaderID = -1; + } + } + + ARBShaderObjects.glLinkProgramARB(state._programID); + checkLinkError(state._programID); + state.setNeedsRefresh(true); + state._needSendShader = false; + } + + private static void checkLinkError(final int programId) { + final IntBuffer compiled = BufferUtils.createIntBuffer(1); + ARBShaderObjects.glGetObjectParameterARB(programId, GL20.GL_LINK_STATUS, compiled); + if (compiled.get(0) == GL11.GL_FALSE) { + ARBShaderObjects.glGetObjectParameterARB(programId, GL20.GL_INFO_LOG_LENGTH, compiled); + final int length = compiled.get(0); + String out = null; + if (length > 0) { + final ByteBuffer infoLog = BufferUtils.createByteBuffer(length); + + ARBShaderObjects.glGetInfoLogARB(programId, compiled, infoLog); + + final byte[] infoBytes = new byte[length]; + infoLog.get(infoBytes); + out = new String(infoBytes); + } + + logger.severe(out); + + throw new Ardor3dException("Error linking GLSL shader: " + out); + } + } + + /** Removes the geometry shader */ + private static void removeGeomShader(final GLSLShaderObjectsState state) { + if (state._geometryShaderID != -1) { + ARBShaderObjects.glDetachObjectARB(state._programID, state._geometryShaderID); + ARBShaderObjects.glDeleteObjectARB(state._geometryShaderID); + } + } + + /** Removes the fragment shader */ + private static void removeFragShader(final GLSLShaderObjectsState state) { + if (state._fragmentShaderID != -1) { + ARBShaderObjects.glDetachObjectARB(state._programID, state._fragmentShaderID); + ARBShaderObjects.glDeleteObjectARB(state._fragmentShaderID); + } + } + + /** + * Removes the vertex shader + */ + private static void removeVertShader(final GLSLShaderObjectsState state) { + if (state._vertexShaderID != -1) { + ARBShaderObjects.glDetachObjectARB(state._programID, state._vertexShaderID); + ARBShaderObjects.glDeleteObjectARB(state._vertexShaderID); + } + } + + /** Removes the tessellation control shader */ + private static void removeTessControlShader(final GLSLShaderObjectsState state) { + if (state._tessellationControlShaderID != -1) { + ARBShaderObjects.glDetachObjectARB(state._programID, state._tessellationControlShaderID); + ARBShaderObjects.glDeleteObjectARB(state._tessellationControlShaderID); + } + } + + /** Removes the tessellation evaluation shader */ + private static void removeTessEvalShader(final GLSLShaderObjectsState state) { + if (state._tessellationEvaluationShaderID != -1) { + ARBShaderObjects.glDetachObjectARB(state._programID, state._tessellationEvaluationShaderID); + ARBShaderObjects.glDeleteObjectARB(state._tessellationEvaluationShaderID); + } + } + + /** + * Check for program errors. If an error is detected, program exits. + * + * @param compiled + * the compiler state for a given shader + * @param id + * shader's id + */ + private static void checkProgramError(final IntBuffer compiled, final int id, final String shaderName) { + if (compiled.get(0) == GL11.GL_FALSE) { + final IntBuffer iVal = BufferUtils.createIntBuffer(1); + ARBShaderObjects.glGetObjectParameterARB(id, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB, iVal); + final int length = iVal.get(0); + String out = null; + + if (length > 0) { + final ByteBuffer infoLog = BufferUtils.createByteBuffer(length); + + ARBShaderObjects.glGetInfoLogARB(id, iVal, infoLog); + + final byte[] infoBytes = new byte[length]; + infoLog.get(infoBytes); + out = new String(infoBytes); + } + + logger.severe(out); + final String nameString = shaderName.equals("") ? "" : " [ " + shaderName + " ]"; + throw new Ardor3dException("Error compiling GLSL shader " + nameString + ": " + out); + + } + } + + public static void apply(final LwjglRenderer renderer, final GLSLShaderObjectsState state) { + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + + if (caps.isGLSLSupported()) { + // Ask for the current state record + final ShaderObjectsStateRecord record = (ShaderObjectsStateRecord) context + .getStateRecord(StateType.GLSLShader); + context.setCurrentState(StateType.GLSLShader, state); + + if (state.isEnabled()) { + if (state._needSendShader) { + sendToGL(state, caps); + } + + if (state._shaderDataLogic != null) { + state._shaderDataLogic.applyData(state, state._mesh, renderer); + } + } + + if (!record.isValid() || record.getReference() != state || state.needsRefresh()) { + record.setReference(state); + if (state.isEnabled() && state._programID != -1) { + // clear any previously existing attributes + clearEnabledAttributes(record); + + // set our current shader + LwjglShaderUtil.useShaderProgram(state._programID, record); + + for (int i = state.getShaderAttributes().size(); --i >= 0;) { + final ShaderVariable shaderVariable = state.getShaderAttributes().get(i); + if (shaderVariable.needsRefresh) { + LwjglShaderUtil.updateAttributeLocation(shaderVariable, state._programID); + shaderVariable.needsRefresh = false; + } + LwjglShaderUtil.updateShaderAttribute(renderer, shaderVariable, state.isUseAttributeVBO()); + } + + for (int i = state.getShaderUniforms().size(); --i >= 0;) { + final ShaderVariable shaderVariable = state.getShaderUniforms().get(i); + if (shaderVariable.needsRefresh) { + LwjglShaderUtil.updateUniformLocation(shaderVariable, state._programID); + LwjglShaderUtil.updateShaderUniform(shaderVariable); + shaderVariable.needsRefresh = false; + } + } + } else { + LwjglShaderUtil.useShaderProgram(0, record); + + clearEnabledAttributes(record); + } + } + + if (!record.isValid()) { + record.validate(); + } + } + } + + private static void clearEnabledAttributes(final ShaderObjectsStateRecord record) { + // go through and disable any enabled attributes + if (!record.enabledAttributes.isEmpty()) { + for (int i = 0, maxI = record.enabledAttributes.size(); i < maxI; i++) { + final ShaderVariable var = record.enabledAttributes.get(i); + if (var.getSize() == 1) { + ARBVertexProgram.glDisableVertexAttribArrayARB(var.variableID); + } else { + for (int j = 0, maxJ = var.getSize(); j < maxJ; j++) { + ARBVertexProgram.glDisableVertexAttribArrayARB(var.variableID + j); + } + } + } + record.enabledAttributes.clear(); + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShadingStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShadingStateUtil.java new file mode 100644 index 0000000..a46aa17 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglShadingStateUtil.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.ShadingState; +import com.ardor3d.renderer.state.ShadingState.ShadingMode; +import com.ardor3d.renderer.state.record.ShadingStateRecord; + +public abstract class LwjglShadingStateUtil { + + public static void apply(final ShadingState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final ShadingStateRecord record = (ShadingStateRecord) context.getStateRecord(StateType.Shading); + context.setCurrentState(StateType.Shading, state); + + // If not enabled, we'll use smooth + final int toApply = state.isEnabled() ? getGLShade(state.getShadingMode()) : GL11.GL_SMOOTH; + // only apply if we're different. Update record to reflect any changes. + if (!record.isValid() || toApply != record.lastShade) { + GL11.glShadeModel(toApply); + record.lastShade = toApply; + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static int getGLShade(final ShadingMode shadeMode) { + switch (shadeMode) { + case Flat: + return GL11.GL_FLAT; + case Smooth: + return GL11.GL_SMOOTH; + } + throw new IllegalStateException("unknown shade mode: " + shadeMode); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglStencilStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglStencilStateUtil.java new file mode 100644 index 0000000..176fc8c --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglStencilStateUtil.java @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.EXTStencilTwoSide; +import org.lwjgl.opengl.EXTStencilWrap; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.StencilState; +import com.ardor3d.renderer.state.StencilState.StencilFunction; +import com.ardor3d.renderer.state.StencilState.StencilOperation; +import com.ardor3d.renderer.state.record.StencilStateRecord; + +public abstract class LwjglStencilStateUtil { + + public static void apply(final StencilState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + final StencilStateRecord record = (StencilStateRecord) context.getStateRecord(StateType.Stencil); + context.setCurrentState(StateType.Stencil, state); + + setEnabled(state.isEnabled(), caps.isTwoSidedStencilSupported() ? state.isUseTwoSided() : false, record, caps); + if (state.isEnabled()) { + if (state.isUseTwoSided() && caps.isTwoSidedStencilSupported()) { + EXTStencilTwoSide.glActiveStencilFaceEXT(GL11.GL_BACK); + applyMask(state.getStencilWriteMaskBack(), record, 2); + applyFunc(getGLStencilFunction(state.getStencilFunctionBack()), state.getStencilReferenceBack(), + state.getStencilFuncMaskBack(), record, 2); + applyOp(getGLStencilOp(state.getStencilOpFailBack(), caps), + getGLStencilOp(state.getStencilOpZFailBack(), caps), + getGLStencilOp(state.getStencilOpZPassBack(), caps), record, 2); + + EXTStencilTwoSide.glActiveStencilFaceEXT(GL11.GL_FRONT); + applyMask(state.getStencilWriteMaskFront(), record, 1); + applyFunc(getGLStencilFunction(state.getStencilFunctionFront()), state.getStencilReferenceFront(), + state.getStencilFuncMaskFront(), record, 1); + applyOp(getGLStencilOp(state.getStencilOpFailFront(), caps), + getGLStencilOp(state.getStencilOpZFailFront(), caps), + getGLStencilOp(state.getStencilOpZPassFront(), caps), record, 1); + } else { + applyMask(state.getStencilWriteMaskFront(), record, 0); + applyFunc(getGLStencilFunction(state.getStencilFunctionFront()), state.getStencilReferenceFront(), + state.getStencilFuncMaskFront(), record, 0); + applyOp(getGLStencilOp(state.getStencilOpFailFront(), caps), + getGLStencilOp(state.getStencilOpZFailFront(), caps), + getGLStencilOp(state.getStencilOpZPassFront(), caps), record, 0); + } + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static int getGLStencilFunction(final StencilFunction function) { + switch (function) { + case Always: + return GL11.GL_ALWAYS; + case Never: + return GL11.GL_NEVER; + case EqualTo: + return GL11.GL_EQUAL; + case NotEqualTo: + return GL11.GL_NOTEQUAL; + case GreaterThan: + return GL11.GL_GREATER; + case GreaterThanOrEqualTo: + return GL11.GL_GEQUAL; + case LessThan: + return GL11.GL_LESS; + case LessThanOrEqualTo: + return GL11.GL_LEQUAL; + } + throw new IllegalArgumentException("unknown function: " + function); + } + + private static int getGLStencilOp(final StencilOperation operation, final ContextCapabilities caps) { + switch (operation) { + case Keep: + return GL11.GL_KEEP; + case DecrementWrap: + if (caps.isStencilWrapSupported()) { + return EXTStencilWrap.GL_DECR_WRAP_EXT; + } + // FALLS THROUGH + case Decrement: + return GL11.GL_DECR; + case IncrementWrap: + if (caps.isStencilWrapSupported()) { + return EXTStencilWrap.GL_INCR_WRAP_EXT; + } + // FALLS THROUGH + case Increment: + return GL11.GL_INCR; + case Invert: + return GL11.GL_INVERT; + case Replace: + return GL11.GL_REPLACE; + case Zero: + return GL11.GL_ZERO; + } + throw new IllegalArgumentException("unknown operation: " + operation); + } + + private static void setEnabled(final boolean enable, final boolean twoSided, final StencilStateRecord record, + final ContextCapabilities caps) { + if (record.isValid()) { + if (enable && !record.enabled) { + GL11.glEnable(GL11.GL_STENCIL_TEST); + } else if (!enable && record.enabled) { + GL11.glDisable(GL11.GL_STENCIL_TEST); + } + } else { + if (enable) { + GL11.glEnable(GL11.GL_STENCIL_TEST); + } else { + GL11.glDisable(GL11.GL_STENCIL_TEST); + } + } + + setTwoSidedEnabled(enable ? twoSided : false, record, caps); + record.enabled = enable; + } + + private static void setTwoSidedEnabled(final boolean enable, final StencilStateRecord record, + final ContextCapabilities caps) { + if (caps.isTwoSidedStencilSupported()) { + if (record.isValid()) { + if (enable && !record.useTwoSided) { + GL11.glEnable(EXTStencilTwoSide.GL_STENCIL_TEST_TWO_SIDE_EXT); + } else if (!enable && record.useTwoSided) { + GL11.glDisable(EXTStencilTwoSide.GL_STENCIL_TEST_TWO_SIDE_EXT); + } + } else { + if (enable) { + GL11.glEnable(EXTStencilTwoSide.GL_STENCIL_TEST_TWO_SIDE_EXT); + } else { + GL11.glDisable(EXTStencilTwoSide.GL_STENCIL_TEST_TWO_SIDE_EXT); + } + } + } + record.useTwoSided = enable; + } + + private static void applyMask(final int writeMask, final StencilStateRecord record, final int face) { + // if (!record.isValid() || writeMask != record.writeMask[face]) { + GL11.glStencilMask(writeMask); + // record.writeMask[face] = writeMask; + // } + } + + private static void applyFunc(final int glfunc, final int stencilRef, final int funcMask, + final StencilStateRecord record, final int face) { + // if (!record.isValid() || glfunc != record.func[face] || stencilRef != record.ref[face] + // || funcMask != record.funcMask[face]) { + GL11.glStencilFunc(glfunc, stencilRef, funcMask); + // record.func[face] = glfunc; + // record.ref[face] = stencilRef; + // record.funcMask[face] = funcMask; + // } + } + + private static void applyOp(final int fail, final int zfail, final int zpass, final StencilStateRecord record, + final int face) { + // if (!record.isValid() || fail != record.fail[face] || zfail != record.zfail[face] + // || zpass != record.zpass[face]) { + GL11.glStencilOp(fail, zfail, zpass); + // record.fail[face] = fail; + // record.zfail[face] = zfail; + // record.zpass[face] = zpass; + // } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglTextureStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglTextureStateUtil.java new file mode 100644 index 0000000..de21789 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglTextureStateUtil.java @@ -0,0 +1,1686 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.Collection; +import java.util.logging.Logger; + +import org.lwjgl.opengl.ARBDepthTexture; +import org.lwjgl.opengl.ARBMultitexture; +import org.lwjgl.opengl.ARBShadow; +import org.lwjgl.opengl.ARBTextureBorderClamp; +import org.lwjgl.opengl.ARBTextureCompression; +import org.lwjgl.opengl.ARBTextureCubeMap; +import org.lwjgl.opengl.ARBTextureEnvCombine; +import org.lwjgl.opengl.ARBTextureMirroredRepeat; +import org.lwjgl.opengl.EXTTextureFilterAnisotropic; +import org.lwjgl.opengl.EXTTextureLODBias; +import org.lwjgl.opengl.EXTTextureMirrorClamp; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.SGISGenerateMipmap; +import org.lwjgl.opengl.Util; +import org.lwjgl.util.glu.GLU; +import org.lwjgl.util.glu.MipMap; + +import com.ardor3d.image.Image; +import com.ardor3d.image.Texture; +import com.ardor3d.image.Texture.ApplyMode; +import com.ardor3d.image.Texture.CombinerFunctionAlpha; +import com.ardor3d.image.Texture.CombinerFunctionRGB; +import com.ardor3d.image.Texture.CombinerOperandAlpha; +import com.ardor3d.image.Texture.CombinerOperandRGB; +import com.ardor3d.image.Texture.CombinerSource; +import com.ardor3d.image.Texture.Type; +import com.ardor3d.image.Texture.WrapAxis; +import com.ardor3d.image.Texture.WrapMode; +import com.ardor3d.image.Texture1D; +import com.ardor3d.image.Texture2D; +import com.ardor3d.image.Texture3D; +import com.ardor3d.image.TextureCubeMap; +import com.ardor3d.image.util.ImageUtils; +import com.ardor3d.math.MathUtils; +import com.ardor3d.math.type.ReadOnlyColorRGBA; +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.TextureState; +import com.ardor3d.renderer.state.record.RendererRecord; +import com.ardor3d.renderer.state.record.TextureRecord; +import com.ardor3d.renderer.state.record.TextureStateRecord; +import com.ardor3d.renderer.state.record.TextureUnitRecord; +import com.ardor3d.scene.state.lwjgl.util.LwjglRendererUtil; +import com.ardor3d.scene.state.lwjgl.util.LwjglTextureUtil; +import com.ardor3d.util.Constants; +import com.ardor3d.util.TextureManager; +import com.ardor3d.util.geom.BufferUtils; +import com.ardor3d.util.stat.StatCollector; +import com.ardor3d.util.stat.StatType; + +public abstract class LwjglTextureStateUtil { + private static final Logger logger = Logger.getLogger(LwjglTextureStateUtil.class.getName()); + + public static void load(final Texture texture, final int unit) { + if (texture == null) { + return; + } + + final RenderContext context = ContextManager.getCurrentContext(); + if (context == null) { + logger.warning("RenderContext is null for texture: " + texture); + return; + } + + final ContextCapabilities caps = context.getCapabilities(); + final TextureStateRecord record = (TextureStateRecord) context.getStateRecord(StateType.Texture); + + // Check we are in the right unit + if (record != null) { + checkAndSetUnit(unit, record, caps); + } + + // Create the texture... + // First, look for a texture in the cache just like ours + final Texture cached = TextureManager.findCachedTexture(texture.getTextureKey()); + + if (cached == null) { + TextureManager.addToCache(texture); + } else { + final int textureId = cached.getTextureIdForContext(context.getGlContextRep()); + if (textureId != 0) { + doTextureBind(cached, unit, false); + return; + } + } + + // Create a new texture id for this texture + final IntBuffer id = BufferUtils.createIntBuffer(1); + id.clear(); + GL11.glGenTextures(id); + final int textureId = id.get(0); + + // store the new id by our current gl context. + texture.setTextureIdForContext(context.getGlContextRep(), textureId); + + update(texture, unit); + } + + /** + * bind texture and upload image data to card + */ + public static void update(final Texture texture, final int unit) { + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + + texture.getTextureKey().setClean(context.getGlContextRep()); + + // our texture type: + final Texture.Type type = texture.getType(); + + // bind our texture id to this unit. + doTextureBind(texture, unit, false); + + // pass image data to OpenGL + final Image image = texture.getImage(); + final boolean hasBorder = texture.hasBorder(); + if (image == null) { + logger.warning("Image data for texture is null."); + } + + // set alignment to support images with width % 4 != 0, as images are + // not aligned + GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); + + // Get texture image data. Not all textures have image data. + // For example, ApplyMode.Combine modes can use primary colors, + // texture output, and constants to modify fragments via the + // texture units. + if (image != null) { + final int maxSize = caps.getMaxTextureSize(); + final int actualWidth = image.getWidth(); + final int actualHeight = image.getHeight(); + + final boolean needsPowerOfTwo = !caps.isNonPowerOfTwoTextureSupported() + && (!MathUtils.isPowerOfTwo(image.getWidth()) || !MathUtils.isPowerOfTwo(image.getHeight())); + if (actualWidth > maxSize || actualHeight > maxSize || needsPowerOfTwo) { + if (needsPowerOfTwo) { + logger.warning("(card unsupported) Attempted to apply texture with size that is not power of 2: " + + image.getWidth() + " x " + image.getHeight()); + } + if (actualWidth > maxSize || actualHeight > maxSize) { + logger.warning("(card unsupported) Attempted to apply texture with size bigger than max texture size [" + + maxSize + "]: " + image.getWidth() + " x " + image.getHeight()); + } + + int w = actualWidth; + if (needsPowerOfTwo) { + w = MathUtils.nearestPowerOfTwo(actualWidth); + } + if (w > maxSize) { + w = maxSize; + } + + int h = actualHeight; + if (needsPowerOfTwo) { + h = MathUtils.nearestPowerOfTwo(actualHeight); + } + if (h > maxSize) { + h = maxSize; + } + logger.warning("Rescaling image to " + w + " x " + h + " !!!"); + + // must rescale image to get "top" mipmap texture image + final int pixFormat = LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()); + final int pixDataType = LwjglTextureUtil.getGLPixelDataType(image.getDataType()); + final int bpp = ImageUtils.getPixelByteSize(image.getDataFormat(), image.getDataType()); + final ByteBuffer scaledImage = BufferUtils.createByteBuffer((w + 4) * h * bpp); + final int error = MipMap.gluScaleImage(pixFormat, actualWidth, actualHeight, pixDataType, + image.getData(0), w, h, pixDataType, scaledImage); + if (error != 0) { + Util.checkGLError(); + } + + image.setWidth(w); + image.setHeight(h); + image.setData(scaledImage); + } + + if (!texture.getMinificationFilter().usesMipMapLevels() && !texture.getTextureStoreFormat().isCompressed()) { + + // Load textures which do not need mipmap auto-generating and + // which aren't using compressed images. + + switch (texture.getType()) { + case TwoDimensional: + // ensure the buffer is ready for reading + image.getData(0).rewind(); + // send top level to card + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getHeight(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0)); + break; + case OneDimensional: + // ensure the buffer is ready for reading + image.getData(0).rewind(); + // send top level to card + GL11.glTexImage1D(GL11.GL_TEXTURE_1D, 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0)); + break; + case ThreeDimensional: + if (caps.isTexture3DSupported()) { + // concat data into single buffer: + int dSize = 0; + int count = 0; + ByteBuffer data = null; + for (int x = 0; x < image.getData().size(); x++) { + if (image.getData(x) != null) { + data = image.getData(x); + dSize += data.limit(); + count++; + } + } + // reuse buffer if we can. + if (count != 1) { + data = BufferUtils.createByteBuffer(dSize); + for (int x = 0; x < image.getData().size(); x++) { + if (image.getData(x) != null) { + data.put(image.getData(x)); + } + } + // ensure the buffer is ready for reading + data.flip(); + } + // send top level to card + GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getHeight(), image.getDepth(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data); + } else { + logger.warning("This card does not support Texture3D."); + } + break; + case CubeMap: + // NOTE: Cubemaps MUST be square, so height is ignored + // on purpose. + if (caps.isTextureCubeMapSupported()) { + for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) { + // ensure the buffer is ready for reading + image.getData(face.ordinal()).rewind(); + // send top level to card + GL11.glTexImage2D(getGLCubeMapFace(face), 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getWidth(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), + image.getData(face.ordinal())); + } + } else { + logger.warning("This card does not support Cubemaps."); + } + break; + } + } else if (texture.getMinificationFilter().usesMipMapLevels() && !image.hasMipmaps() + && !texture.getTextureStoreFormat().isCompressed()) { + + // For textures which need mipmaps auto-generating and which + // aren't using compressed images, generate the mipmaps. + // A new mipmap builder may be needed to build mipmaps for + // compressed textures. + + if (caps.isAutomaticMipmapsSupported()) { + // Flag the card to generate mipmaps + GL11.glTexParameteri(getGLType(type), SGISGenerateMipmap.GL_GENERATE_MIPMAP_SGIS, GL11.GL_TRUE); + } + + switch (type) { + case TwoDimensional: + // ensure the buffer is ready for reading + image.getData(0).rewind(); + if (caps.isAutomaticMipmapsSupported()) { + // send top level to card + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getHeight(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0)); + } else { + // send to card + GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getHeight(), + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0)); + } + break; + case OneDimensional: + // ensure the buffer is ready for reading + image.getData(0).rewind(); + if (caps.isAutomaticMipmapsSupported()) { + // send top level to card + GL11.glTexImage1D(GL11.GL_TEXTURE_1D, 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0)); + } else { + // Note: LWJGL's GLU class does not support + // gluBuild1DMipmaps. + logger.warning("non-fbo 1d mipmap generation is not currently supported. Use DDS or a non-mipmap minification filter."); + return; + } + break; + case ThreeDimensional: + if (caps.isTexture3DSupported()) { + if (caps.isAutomaticMipmapsSupported()) { + // concat data into single buffer: + int dSize = 0; + int count = 0; + ByteBuffer data = null; + for (int x = 0; x < image.getData().size(); x++) { + if (image.getData(x) != null) { + data = image.getData(x); + dSize += data.limit(); + count++; + } + } + // reuse buffer if we can. + if (count != 1) { + data = BufferUtils.createByteBuffer(dSize); + for (int x = 0; x < image.getData().size(); x++) { + if (image.getData(x) != null) { + data.put(image.getData(x)); + } + } + // ensure the buffer is ready for reading + data.flip(); + } + // send top level to card + GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getHeight(), image.getDepth(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data); + } else { + // Note: LWJGL's GLU class does not support + // gluBuild3DMipmaps. + logger.warning("non-fbo 3d mipmap generation is not currently supported. Use DDS or a non-mipmap minification filter."); + return; + } + } else { + logger.warning("This card does not support Texture3D."); + return; + } + break; + case CubeMap: + // NOTE: Cubemaps MUST be square, so height is ignored + // on purpose. + if (caps.isTextureCubeMapSupported()) { + if (caps.isAutomaticMipmapsSupported()) { + for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) { + // ensure the buffer is ready for reading + image.getData(face.ordinal()).rewind(); + // send top level to card + GL11.glTexImage2D(getGLCubeMapFace(face), 0, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getWidth(), hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), + image.getData(face.ordinal())); + } + } else { + for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) { + // ensure the buffer is ready for reading + image.getData(face.ordinal()).rewind(); + // send to card + GLU.gluBuild2DMipmaps(getGLCubeMapFace(face), + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + image.getWidth(), image.getWidth(), + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), + image.getData(face.ordinal())); + } + } + } else { + logger.warning("This card does not support Cubemaps."); + return; + } + break; + } + + if (texture.getTextureMaxLevel() >= 0) { + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, texture.getTextureMaxLevel()); + } + } else { + // Here we handle textures that are either compressed or have predefined mipmaps. + // Get mipmap data sizes and amount of mipmaps to send to opengl. Then loop through all mipmaps and send + // them. + int[] mipSizes = image.getMipMapByteSizes(); + ByteBuffer data = null; + + if (type == Type.CubeMap) { + if (caps.isTextureCubeMapSupported()) { + for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) { + data = image.getData(face.ordinal()); + int pos = 0; + int max = 1; + + if (mipSizes == null) { + mipSizes = new int[] { data.capacity() }; + } else if (texture.getMinificationFilter().usesMipMapLevels()) { + max = mipSizes.length; + } + + // set max mip level + GL11.glTexParameteri(getGLCubeMapFace(face), GL12.GL_TEXTURE_MAX_LEVEL, max - 1); + + for (int m = 0; m < max; m++) { + final int width = Math.max(1, image.getWidth() >> m); + final int height = Math.max(1, image.getHeight() >> m); + + data.position(pos); + data.limit(pos + mipSizes[m]); + + if (texture.getTextureStoreFormat().isCompressed()) { + ARBTextureCompression.glCompressedTexImage2DARB(getGLCubeMapFace(face), m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, height, hasBorder ? 1 : 0, data); + } else { + GL11.glTexImage2D(getGLCubeMapFace(face), m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, height, hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data); + } + pos += mipSizes[m]; + } + } + } else { + logger.warning("This card does not support CubeMaps."); + return; + } + } else { + data = image.getData(0); + int pos = 0; + int max = 1; + + if (mipSizes == null) { + mipSizes = new int[] { data.capacity() }; + } else if (texture.getMinificationFilter().usesMipMapLevels()) { + max = mipSizes.length; + } + + // Set max mip level + switch (type) { + case TwoDimensional: + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, max - 1); + break; + case ThreeDimensional: + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_MAX_LEVEL, max - 1); + break; + case OneDimensional: + GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL12.GL_TEXTURE_MAX_LEVEL, max - 1); + break; + } + + if (type == Type.ThreeDimensional) { + if (caps.isTexture3DSupported()) { + // concat data into single buffer: + int dSize = 0; + int count = 0; + for (int x = 0; x < image.getData().size(); x++) { + if (image.getData(x) != null) { + data = image.getData(x); + dSize += data.limit(); + count++; + } + } + // reuse buffer if we can. + if (count != 1) { + data = BufferUtils.createByteBuffer(dSize); + for (int x = 0; x < image.getData().size(); x++) { + if (image.getData(x) != null) { + data.put(image.getData(x)); + } + } + // ensure the buffer is ready for reading + data.flip(); + } + } else { + logger.warning("This card does not support Texture3D."); + return; + } + } + + for (int m = 0; m < max; m++) { + final int width = Math.max(1, image.getWidth() >> m); + final int height = Math.max(1, image.getHeight() >> m); + + data.position(pos); + data.limit(pos + mipSizes[m]); + + switch (type) { + case TwoDimensional: + if (texture.getTextureStoreFormat().isCompressed()) { + ARBTextureCompression.glCompressedTexImage2DARB(GL11.GL_TEXTURE_2D, m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, height, hasBorder ? 1 : 0, data); + } else { + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, height, hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data); + } + break; + case OneDimensional: + if (texture.getTextureStoreFormat().isCompressed()) { + ARBTextureCompression.glCompressedTexImage1DARB(GL11.GL_TEXTURE_1D, m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, hasBorder ? 1 : 0, data); + } else { + GL11.glTexImage1D(GL11.GL_TEXTURE_1D, m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data); + } + break; + case ThreeDimensional: + final int depth = Math.max(1, image.getDepth() >> m); + // already checked for support above... + if (texture.getTextureStoreFormat().isCompressed()) { + ARBTextureCompression.glCompressedTexImage3DARB(GL12.GL_TEXTURE_3D, m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, height, depth, hasBorder ? 1 : 0, data); + } else { + GL12.glTexImage3D(GL12.GL_TEXTURE_3D, m, + LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()), + width, height, depth, hasBorder ? 1 : 0, + LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()), + LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data); + } + break; + } + pos += mipSizes[m]; + } + } + if (data != null) { + data.clear(); + } + } + } + } + + public static void apply(final TextureState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + final TextureStateRecord record = (TextureStateRecord) context.getStateRecord(StateType.Texture); + context.setCurrentState(StateType.Texture, state); + + if (state.isEnabled()) { + + Texture texture; + Texture.Type type; + TextureUnitRecord unitRecord; + TextureRecord texRecord; + + final int glHint = LwjglTextureUtil.getPerspHint(state.getCorrectionType()); + if (!record.isValid() || record.hint != glHint) { + // set up correction mode + GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, glHint); + record.hint = glHint; + } + + // loop through all available texture units... + for (int i = 0; i < caps.getNumberOfTotalTextureUnits(); i++) { + unitRecord = record.units[i]; + + // grab a texture for this unit, if available + texture = state.getTexture(i); + + // pull our texture id for this texture, for this context. + int textureId = texture != null ? texture.getTextureIdForContext(context.getGlContextRep()) : 0; + + // check for invalid textures - ones that have no opengl id and + // no image data + if (texture != null && textureId == 0 && texture.getImage() == null) { + texture = null; + } + + // null textures above fixed limit do not need to be disabled + // since they are not really part of the pipeline. + if (texture == null) { + if (i >= caps.getNumberOfFixedTextureUnits()) { + continue; + } else { + // a null texture indicates no texturing at this unit + // Disable texturing on this unit if enabled. + disableTexturing(unitRecord, record, i, caps); + + if (i < state._keyCache.length) { + state._keyCache[i] = null; + } + + // next texture! + continue; + } + } + + type = texture.getType(); + + // disable other texturing types for this unit, if enabled. + disableTexturing(unitRecord, record, i, type, caps); + + // Time to bind the texture, so see if we need to load in image + // data for this texture. + if (textureId == 0) { + // texture not yet loaded. + // this will load and bind and set the records... + load(texture, i); + textureId = texture.getTextureIdForContext(context.getGlContextRep()); + if (textureId == 0) { + continue; + } + } else if (texture.isDirty(context.getGlContextRep())) { + update(texture, i); + textureId = texture.getTextureIdForContext(context.getGlContextRep()); + if (textureId == 0) { + continue; + } + } else { + // texture already exists in OpenGL, just bind it if needed + if (!unitRecord.isValid() || unitRecord.boundTexture != textureId) { + checkAndSetUnit(i, record, caps); + GL11.glBindTexture(getGLType(type), textureId); + if (Constants.stats) { + StatCollector.addStat(StatType.STAT_TEXTURE_BINDS, 1); + } + unitRecord.boundTexture = textureId; + } + } + + // Use the Java Integer object for the getTextureRecord call to avoid + // boxing/unboxing ints for map lookups. + final Integer textureIdInteger = texture.getTextureIdForContextAsInteger(context.getGlContextRep()); + + // Grab our record for this texture + texRecord = record.getTextureRecord(textureIdInteger, texture.getType()); + + // Set the keyCache value for this unit of this texture state + // This is done so during state comparison we don't have to + // spend a lot of time pulling out classes and finding field + // data. + state._keyCache[i] = texture.getTextureKey(); + + // Some texture things only apply to fixed function pipeline + if (i < caps.getNumberOfFixedTextureUnits()) { + + // Enable 2D texturing on this unit if not enabled. + if (!unitRecord.isValid() || !unitRecord.enabled[type.ordinal()]) { + checkAndSetUnit(i, record, caps); + GL11.glEnable(getGLType(type)); + unitRecord.enabled[type.ordinal()] = true; + } + + // Set our blend color, if needed. + applyBlendColor(texture, unitRecord, i, record, caps); + + // Set the texture environment mode if this unit isn't + // already set properly + applyEnvMode(texture.getApply(), unitRecord, i, record, caps); + + // If our mode is combine, and we support multitexturing + // apply combine settings. + if (texture.getApply() == ApplyMode.Combine && caps.isMultitextureSupported() + && caps.isEnvCombineSupported()) { + applyCombineFactors(texture, unitRecord, i, record, caps); + } + } + + // Other items only apply to textures below the frag unit limit + if (i < caps.getNumberOfFragmentTextureUnits()) { + + // texture specific params + applyFilter(texture, texRecord, i, record, caps); + applyWrap(texture, texRecord, i, record, caps); + applyShadow(texture, texRecord, i, record, caps); + + // Set our border color, if needed. + applyBorderColor(texture, texRecord, i, record); + + // all states have now been applied for a tex record, so we + // can safely make it valid + if (!texRecord.isValid()) { + texRecord.validate(); + } + + } + + // Other items only apply to textures below the frag tex coord + // unit limit + if (i < caps.getNumberOfFragmentTexCoordUnits()) { + + // Now time to play with texture matrices + // Determine which transforms to do. + applyTextureTransforms(texture, i, record, caps); + + // Now let's look at automatic texture coordinate + // generation. + applyTexCoordGeneration(texture, unitRecord, i, record, caps); + + // Set our texture lod bias, if needed. + applyLodBias(texture, unitRecord, i, record, caps); + } + + } + + } else { + // turn off texturing + TextureUnitRecord unitRecord; + + if (caps.isMultitextureSupported()) { + for (int i = 0; i < caps.getNumberOfFixedTextureUnits(); i++) { + unitRecord = record.units[i]; + disableTexturing(unitRecord, record, i, caps); + } + } else { + unitRecord = record.units[0]; + disableTexturing(unitRecord, record, 0, caps); + } + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void disableTexturing(final TextureUnitRecord unitRecord, final TextureStateRecord record, + final int unit, final Type exceptedType, final ContextCapabilities caps) { + if (exceptedType != Type.TwoDimensional) { + if (!unitRecord.isValid() || unitRecord.enabled[Type.TwoDimensional.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(GL11.GL_TEXTURE_2D); + unitRecord.enabled[Type.TwoDimensional.ordinal()] = false; + } + } + + if (exceptedType != Type.OneDimensional) { + if (!unitRecord.isValid() || unitRecord.enabled[Type.OneDimensional.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(GL11.GL_TEXTURE_1D); + unitRecord.enabled[Type.OneDimensional.ordinal()] = false; + } + } + + if (caps.isTexture3DSupported() && exceptedType != Type.ThreeDimensional) { + if (!unitRecord.isValid() || unitRecord.enabled[Type.ThreeDimensional.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(GL12.GL_TEXTURE_3D); + unitRecord.enabled[Type.ThreeDimensional.ordinal()] = false; + } + } + + if (caps.isTextureCubeMapSupported() && exceptedType != Type.CubeMap) { + if (!unitRecord.isValid() || unitRecord.enabled[Type.CubeMap.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_ARB); + unitRecord.enabled[Type.CubeMap.ordinal()] = false; + } + } + + } + + private static void disableTexturing(final TextureUnitRecord unitRecord, final TextureStateRecord record, + final int unit, final ContextCapabilities caps) { + if (!unitRecord.isValid() || unitRecord.enabled[Type.TwoDimensional.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(GL11.GL_TEXTURE_2D); + unitRecord.enabled[Type.TwoDimensional.ordinal()] = false; + } + + if (!unitRecord.isValid() || unitRecord.enabled[Type.OneDimensional.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(GL11.GL_TEXTURE_1D); + unitRecord.enabled[Type.OneDimensional.ordinal()] = false; + } + + if (caps.isTexture3DSupported()) { + if (!unitRecord.isValid() || unitRecord.enabled[Type.ThreeDimensional.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(GL12.GL_TEXTURE_3D); + unitRecord.enabled[Type.ThreeDimensional.ordinal()] = false; + } + } + + if (caps.isTextureCubeMapSupported()) { + if (!unitRecord.isValid() || unitRecord.enabled[Type.CubeMap.ordinal()]) { + // Check we are in the right unit + checkAndSetUnit(unit, record, caps); + GL11.glDisable(ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_ARB); + unitRecord.enabled[Type.CubeMap.ordinal()] = false; + } + } + + } + + public static void applyCombineFactors(final Texture texture, final TextureUnitRecord unitRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + // check that this is a valid fixed function unit. glTexEnv is only + // supported for unit < GL_MAX_TEXTURE_UNITS + if (unit >= caps.getNumberOfFixedTextureUnits()) { + return; + } + + // first thing's first... if we are doing dot3 and don't + // support it, disable this texture. + boolean checked = false; + if (!caps.isEnvDot3TextureCombineSupported() + && (texture.getCombineFuncRGB() == CombinerFunctionRGB.Dot3RGB || texture.getCombineFuncRGB() == CombinerFunctionRGB.Dot3RGBA)) { + + // disable + disableTexturing(unitRecord, record, unit, caps); + + // No need to continue + return; + } + + // Okay, now let's set our scales if we need to: + // First RGB Combine scale + if (!unitRecord.isValid() || unitRecord.envRGBScale != texture.getCombineScaleRGB()) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_RGB_SCALE_ARB, texture.getCombineScaleRGB() + .floatValue()); + unitRecord.envRGBScale = texture.getCombineScaleRGB(); + } + // Then Alpha Combine scale + if (!unitRecord.isValid() || unitRecord.envAlphaScale != texture.getCombineScaleAlpha()) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_ALPHA_SCALE, texture.getCombineScaleAlpha().floatValue()); + unitRecord.envAlphaScale = texture.getCombineScaleAlpha(); + } + + // Time to set the RGB combines + final CombinerFunctionRGB rgbCombineFunc = texture.getCombineFuncRGB(); + if (!unitRecord.isValid() || unitRecord.rgbCombineFunc != rgbCombineFunc) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_COMBINE_RGB_ARB, + LwjglTextureUtil.getGLCombineFuncRGB(rgbCombineFunc)); + unitRecord.rgbCombineFunc = rgbCombineFunc; + } + + CombinerSource combSrcRGB = texture.getCombineSrc0RGB(); + if (!unitRecord.isValid() || unitRecord.combSrcRGB0 != combSrcRGB) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_SOURCE0_RGB_ARB, + LwjglTextureUtil.getGLCombineSrc(combSrcRGB)); + unitRecord.combSrcRGB0 = combSrcRGB; + } + + CombinerOperandRGB combOpRGB = texture.getCombineOp0RGB(); + if (!unitRecord.isValid() || unitRecord.combOpRGB0 != combOpRGB) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_OPERAND0_RGB_ARB, + LwjglTextureUtil.getGLCombineOpRGB(combOpRGB)); + unitRecord.combOpRGB0 = combOpRGB; + } + + // We only need to do Arg1 or Arg2 if we aren't in Replace mode + if (rgbCombineFunc != CombinerFunctionRGB.Replace) { + + combSrcRGB = texture.getCombineSrc1RGB(); + if (!unitRecord.isValid() || unitRecord.combSrcRGB1 != combSrcRGB) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_SOURCE1_RGB_ARB, + LwjglTextureUtil.getGLCombineSrc(combSrcRGB)); + unitRecord.combSrcRGB1 = combSrcRGB; + } + + combOpRGB = texture.getCombineOp1RGB(); + if (!unitRecord.isValid() || unitRecord.combOpRGB1 != combOpRGB) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_OPERAND1_RGB_ARB, + LwjglTextureUtil.getGLCombineOpRGB(combOpRGB)); + unitRecord.combOpRGB1 = combOpRGB; + } + + // We only need to do Arg2 if we are in Interpolate mode + if (rgbCombineFunc == CombinerFunctionRGB.Interpolate) { + + combSrcRGB = texture.getCombineSrc2RGB(); + if (!unitRecord.isValid() || unitRecord.combSrcRGB2 != combSrcRGB) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_SOURCE2_RGB_ARB, + LwjglTextureUtil.getGLCombineSrc(combSrcRGB)); + unitRecord.combSrcRGB2 = combSrcRGB; + } + + combOpRGB = texture.getCombineOp2RGB(); + if (!unitRecord.isValid() || unitRecord.combOpRGB2 != combOpRGB) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_OPERAND2_RGB_ARB, + LwjglTextureUtil.getGLCombineOpRGB(combOpRGB)); + unitRecord.combOpRGB2 = combOpRGB; + } + + } + } + + // Now Alpha combines + final CombinerFunctionAlpha alphaCombineFunc = texture.getCombineFuncAlpha(); + if (!unitRecord.isValid() || unitRecord.alphaCombineFunc != alphaCombineFunc) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_COMBINE_ALPHA_ARB, + LwjglTextureUtil.getGLCombineFuncAlpha(alphaCombineFunc)); + unitRecord.alphaCombineFunc = alphaCombineFunc; + } + + CombinerSource combSrcAlpha = texture.getCombineSrc0Alpha(); + if (!unitRecord.isValid() || unitRecord.combSrcAlpha0 != combSrcAlpha) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_SOURCE0_ALPHA_ARB, + LwjglTextureUtil.getGLCombineSrc(combSrcAlpha)); + unitRecord.combSrcAlpha0 = combSrcAlpha; + } + + CombinerOperandAlpha combOpAlpha = texture.getCombineOp0Alpha(); + if (!unitRecord.isValid() || unitRecord.combOpAlpha0 != combOpAlpha) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_OPERAND0_ALPHA_ARB, + LwjglTextureUtil.getGLCombineOpAlpha(combOpAlpha)); + unitRecord.combOpAlpha0 = combOpAlpha; + } + + // We only need to do Arg1 or Arg2 if we aren't in Replace mode + if (alphaCombineFunc != CombinerFunctionAlpha.Replace) { + + combSrcAlpha = texture.getCombineSrc1Alpha(); + if (!unitRecord.isValid() || unitRecord.combSrcAlpha1 != combSrcAlpha) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_SOURCE1_ALPHA_ARB, + LwjglTextureUtil.getGLCombineSrc(combSrcAlpha)); + unitRecord.combSrcAlpha1 = combSrcAlpha; + } + + combOpAlpha = texture.getCombineOp1Alpha(); + if (!unitRecord.isValid() || unitRecord.combOpAlpha1 != combOpAlpha) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_OPERAND1_ALPHA_ARB, + LwjglTextureUtil.getGLCombineOpAlpha(combOpAlpha)); + unitRecord.combOpAlpha1 = combOpAlpha; + } + + // We only need to do Arg2 if we are in Interpolate mode + if (alphaCombineFunc == CombinerFunctionAlpha.Interpolate) { + + combSrcAlpha = texture.getCombineSrc2Alpha(); + if (!unitRecord.isValid() || unitRecord.combSrcAlpha2 != combSrcAlpha) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_SOURCE2_ALPHA_ARB, + LwjglTextureUtil.getGLCombineSrc(combSrcAlpha)); + unitRecord.combSrcAlpha2 = combSrcAlpha; + } + + combOpAlpha = texture.getCombineOp2Alpha(); + if (!unitRecord.isValid() || unitRecord.combOpAlpha2 != combOpAlpha) { + if (!checked) { + checkAndSetUnit(unit, record, caps); + checked = true; + } + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, ARBTextureEnvCombine.GL_OPERAND2_ALPHA_ARB, + LwjglTextureUtil.getGLCombineOpAlpha(combOpAlpha)); + unitRecord.combOpAlpha2 = combOpAlpha; + } + } + } + } + + public static void applyEnvMode(final ApplyMode mode, final TextureUnitRecord unitRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + if (!unitRecord.isValid() || unitRecord.envMode != mode) { + checkAndSetUnit(unit, record, caps); + GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, LwjglTextureUtil.getGLEnvMode(mode)); + unitRecord.envMode = mode; + } + } + + public static void applyBlendColor(final Texture texture, final TextureUnitRecord unitRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + final ReadOnlyColorRGBA texBlend = texture.getConstantColor(); + if (!unitRecord.isValid() || !unitRecord.blendColor.equals(texBlend)) { + checkAndSetUnit(unit, record, caps); + TextureRecord.colorBuffer.clear(); + TextureRecord.colorBuffer.put(texBlend.getRed()).put(texBlend.getGreen()).put(texBlend.getBlue()) + .put(texBlend.getAlpha()); + TextureRecord.colorBuffer.rewind(); + GL11.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, TextureRecord.colorBuffer); + unitRecord.blendColor.set(texBlend); + } + } + + public static void applyLodBias(final Texture texture, final TextureUnitRecord unitRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + if (caps.isTextureLodBiasSupported()) { + final float bias = texture.getLodBias() < caps.getMaxLodBias() ? texture.getLodBias() : caps + .getMaxLodBias(); + if (!unitRecord.isValid() || unitRecord.lodBias != bias) { + checkAndSetUnit(unit, record, caps); + GL11.glTexEnvf(EXTTextureLODBias.GL_TEXTURE_FILTER_CONTROL_EXT, + EXTTextureLODBias.GL_TEXTURE_LOD_BIAS_EXT, bias); + unitRecord.lodBias = bias; + } + } + } + + public static void applyBorderColor(final Texture texture, final TextureRecord texRecord, final int unit, + final TextureStateRecord record) { + final ReadOnlyColorRGBA texBorder = texture.getBorderColor(); + if (!texRecord.isValid() || !texRecord.borderColor.equals(texBorder)) { + TextureRecord.colorBuffer.clear(); + TextureRecord.colorBuffer.put(texBorder.getRed()).put(texBorder.getGreen()).put(texBorder.getBlue()) + .put(texBorder.getAlpha()); + TextureRecord.colorBuffer.rewind(); + GL11.glTexParameter(getGLType(texture.getType()), GL11.GL_TEXTURE_BORDER_COLOR, TextureRecord.colorBuffer); + texRecord.borderColor.set(texBorder); + } + } + + public static void applyTextureTransforms(final Texture texture, final int unit, final TextureStateRecord record, + final ContextCapabilities caps) { + final boolean needsReset = !record.units[unit].identityMatrix; + + // Should we apply the transform? + final boolean doTrans = !texture.getTextureMatrix().isIdentity(); + + // Now do them. + final RendererRecord matRecord = ContextManager.getCurrentContext().getRendererRecord(); + if (doTrans) { + checkAndSetUnit(unit, record, caps); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_TEXTURE); + + record.tmp_matrixBuffer.rewind(); + texture.getTextureMatrix().toDoubleBuffer(record.tmp_matrixBuffer, true); + record.tmp_matrixBuffer.rewind(); + GL11.glLoadMatrix(record.tmp_matrixBuffer); + + record.units[unit].identityMatrix = false; + } else if (needsReset) { + checkAndSetUnit(unit, record, caps); + LwjglRendererUtil.switchMode(matRecord, GL11.GL_TEXTURE); + GL11.glLoadIdentity(); + record.units[unit].identityMatrix = true; + } + // Switch back to the modelview matrix for further operations + LwjglRendererUtil.switchMode(matRecord, GL11.GL_MODELVIEW); + } + + public static void applyTexCoordGeneration(final Texture texture, final TextureUnitRecord unitRecord, + final int unit, final TextureStateRecord record, final ContextCapabilities caps) { + switch (texture.getEnvironmentalMapMode()) { + case None: + // No coordinate generation + setTextureGen(unitRecord, unit, record, caps, false, false, false, false); + break; + case SphereMap: + // generate spherical texture coordinates + if (!unitRecord.isValid() || unitRecord.textureGenSMode != GL11.GL_SPHERE_MAP) { + checkAndSetUnit(unit, record, caps); + + GL11.glTexGeni(GL11.GL_S, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_SPHERE_MAP); + unitRecord.textureGenSMode = GL11.GL_SPHERE_MAP; + + GL11.glTexGeni(GL11.GL_T, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_SPHERE_MAP); + unitRecord.textureGenTMode = GL11.GL_SPHERE_MAP; + } + + setTextureGen(unitRecord, unit, record, caps, true, true, false, false); + break; + case NormalMap: + // generate normals based texture coordinates + if (!unitRecord.isValid() || unitRecord.textureGenSMode != ARBTextureCubeMap.GL_NORMAL_MAP_ARB) { + checkAndSetUnit(unit, record, caps); + GL11.glTexGeni(GL11.GL_S, GL11.GL_TEXTURE_GEN_MODE, ARBTextureCubeMap.GL_NORMAL_MAP_ARB); + unitRecord.textureGenSMode = ARBTextureCubeMap.GL_NORMAL_MAP_ARB; + + GL11.glTexGeni(GL11.GL_T, GL11.GL_TEXTURE_GEN_MODE, ARBTextureCubeMap.GL_NORMAL_MAP_ARB); + unitRecord.textureGenTMode = ARBTextureCubeMap.GL_NORMAL_MAP_ARB; + + GL11.glTexGeni(GL11.GL_R, GL11.GL_TEXTURE_GEN_MODE, ARBTextureCubeMap.GL_NORMAL_MAP_ARB); + unitRecord.textureGenRMode = ARBTextureCubeMap.GL_NORMAL_MAP_ARB; + } + + setTextureGen(unitRecord, unit, record, caps, true, true, true, false); + break; + case ReflectionMap: + // generate reflection texture coordinates + if (!unitRecord.isValid() || unitRecord.textureGenSMode != ARBTextureCubeMap.GL_REFLECTION_MAP_ARB) { + checkAndSetUnit(unit, record, caps); + + GL11.glTexGeni(GL11.GL_S, GL11.GL_TEXTURE_GEN_MODE, ARBTextureCubeMap.GL_REFLECTION_MAP_ARB); + unitRecord.textureGenSMode = ARBTextureCubeMap.GL_REFLECTION_MAP_ARB; + + GL11.glTexGeni(GL11.GL_T, GL11.GL_TEXTURE_GEN_MODE, ARBTextureCubeMap.GL_REFLECTION_MAP_ARB); + unitRecord.textureGenTMode = ARBTextureCubeMap.GL_REFLECTION_MAP_ARB; + + GL11.glTexGeni(GL11.GL_R, GL11.GL_TEXTURE_GEN_MODE, ARBTextureCubeMap.GL_REFLECTION_MAP_ARB); + unitRecord.textureGenRMode = ARBTextureCubeMap.GL_REFLECTION_MAP_ARB; + } + + setTextureGen(unitRecord, unit, record, caps, true, true, true, false); + break; + case EyeLinear: + // do here because we don't check planes + checkAndSetUnit(unit, record, caps); + + // generate eye linear texture coordinates + if (!unitRecord.isValid() || unitRecord.textureGenSMode != GL11.GL_EYE_LINEAR) { + GL11.glTexGeni(GL11.GL_S, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_EYE_LINEAR); + unitRecord.textureGenSMode = GL11.GL_EYE_LINEAR; + + GL11.glTexGeni(GL11.GL_T, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_EYE_LINEAR); + unitRecord.textureGenTMode = GL11.GL_EYE_LINEAR; + + GL11.glTexGeni(GL11.GL_R, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_EYE_LINEAR); + unitRecord.textureGenRMode = GL11.GL_EYE_LINEAR; + + GL11.glTexGeni(GL11.GL_Q, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_EYE_LINEAR); + unitRecord.textureGenQMode = GL11.GL_EYE_LINEAR; + } + + record.prepPlane(texture.getEnvPlaneS(), TextureStateRecord.DEFAULT_S_PLANE); + GL11.glTexGen(GL11.GL_S, GL11.GL_EYE_PLANE, record.plane); + record.prepPlane(texture.getEnvPlaneT(), TextureStateRecord.DEFAULT_T_PLANE); + GL11.glTexGen(GL11.GL_T, GL11.GL_EYE_PLANE, record.plane); + record.prepPlane(texture.getEnvPlaneR(), TextureStateRecord.DEFAULT_R_PLANE); + GL11.glTexGen(GL11.GL_R, GL11.GL_EYE_PLANE, record.plane); + record.prepPlane(texture.getEnvPlaneQ(), TextureStateRecord.DEFAULT_Q_PLANE); + GL11.glTexGen(GL11.GL_Q, GL11.GL_EYE_PLANE, record.plane); + + setTextureGen(unitRecord, unit, record, caps, true, true, true, true); + break; + case ObjectLinear: + // do here because we don't check planes + checkAndSetUnit(unit, record, caps); + + // generate object linear texture coordinates + if (!unitRecord.isValid() || unitRecord.textureGenSMode != GL11.GL_OBJECT_LINEAR) { + GL11.glTexGeni(GL11.GL_S, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_OBJECT_LINEAR); + unitRecord.textureGenSMode = GL11.GL_OBJECT_LINEAR; + + GL11.glTexGeni(GL11.GL_T, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_OBJECT_LINEAR); + unitRecord.textureGenTMode = GL11.GL_OBJECT_LINEAR; + + GL11.glTexGeni(GL11.GL_R, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_OBJECT_LINEAR); + unitRecord.textureGenRMode = GL11.GL_OBJECT_LINEAR; + + GL11.glTexGeni(GL11.GL_Q, GL11.GL_TEXTURE_GEN_MODE, GL11.GL_OBJECT_LINEAR); + unitRecord.textureGenQMode = GL11.GL_OBJECT_LINEAR; + } + + record.prepPlane(texture.getEnvPlaneS(), TextureStateRecord.DEFAULT_S_PLANE); + GL11.glTexGen(GL11.GL_S, GL11.GL_OBJECT_PLANE, record.plane); + record.prepPlane(texture.getEnvPlaneT(), TextureStateRecord.DEFAULT_T_PLANE); + GL11.glTexGen(GL11.GL_T, GL11.GL_OBJECT_PLANE, record.plane); + record.prepPlane(texture.getEnvPlaneR(), TextureStateRecord.DEFAULT_R_PLANE); + GL11.glTexGen(GL11.GL_R, GL11.GL_OBJECT_PLANE, record.plane); + record.prepPlane(texture.getEnvPlaneQ(), TextureStateRecord.DEFAULT_Q_PLANE); + GL11.glTexGen(GL11.GL_Q, GL11.GL_OBJECT_PLANE, record.plane); + + setTextureGen(unitRecord, unit, record, caps, true, true, true, true); + break; + } + } + + private static void setTextureGen(final TextureUnitRecord unitRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps, final boolean genS, final boolean genT, + final boolean genR, final boolean genQ) { + if (!unitRecord.isValid()) { + checkAndSetUnit(unit, record, caps); + + if (genS) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_S); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_S); + } + if (genT) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_T); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_T); + } + if (genR) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_R); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_R); + } + if (genQ) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_Q); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_Q); + } + } else { + if (genS != unitRecord.textureGenS) { + checkAndSetUnit(unit, record, caps); + if (genS) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_S); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_S); + } + } + if (genT != unitRecord.textureGenT) { + checkAndSetUnit(unit, record, caps); + if (genT) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_T); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_T); + } + } + if (genR != unitRecord.textureGenR) { + checkAndSetUnit(unit, record, caps); + if (genR) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_R); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_R); + } + } + if (genQ != unitRecord.textureGenQ) { + checkAndSetUnit(unit, record, caps); + if (genQ) { + GL11.glEnable(GL11.GL_TEXTURE_GEN_Q); + } else { + GL11.glDisable(GL11.GL_TEXTURE_GEN_Q); + } + } + } + + unitRecord.textureGenS = genS; + unitRecord.textureGenT = genT; + unitRecord.textureGenR = genR; + unitRecord.textureGenQ = genQ; + } + + // If we support multtexturing, specify the unit we are affecting. + public static void checkAndSetUnit(final int unit, final TextureStateRecord record, final ContextCapabilities caps) { + // No need to worry about valid record, since invalidate sets record's + // currentUnit to -1. + if (record.currentUnit != unit) { + if (unit >= caps.getNumberOfTotalTextureUnits() || !caps.isMultitextureSupported() || unit < 0) { + // ignore this request as it is not valid for the user's hardware. + return; + } + ARBMultitexture.glActiveTextureARB(ARBMultitexture.GL_TEXTURE0_ARB + unit); + record.currentUnit = unit; + } + } + + /** + * Check if the filter settings of this particular texture have been changed and apply as needed. + * + * @param texture + * our texture object + * @param texRecord + * our record of the last state of the texture in gl + * @param record + */ + public static void applyShadow(final Texture texture, final TextureRecord texRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + final Type type = texture.getType(); + + if (caps.isDepthTextureSupported()) { + final int depthMode = LwjglTextureUtil.getGLDepthTextureMode(texture.getDepthMode()); + // set up magnification filter + if (!texRecord.isValid() || texRecord.depthTextureMode != depthMode) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(getGLType(type), ARBDepthTexture.GL_DEPTH_TEXTURE_MODE_ARB, depthMode); + texRecord.depthTextureMode = depthMode; + } + } + + if (caps.isARBShadowSupported()) { + final int depthCompareMode = LwjglTextureUtil.getGLDepthTextureCompareMode(texture.getDepthCompareMode()); + // set up magnification filter + if (!texRecord.isValid() || texRecord.depthTextureCompareMode != depthCompareMode) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(getGLType(type), ARBShadow.GL_TEXTURE_COMPARE_MODE_ARB, depthCompareMode); + texRecord.depthTextureCompareMode = depthCompareMode; + } + + final int depthCompareFunc = LwjglTextureUtil.getGLDepthTextureCompareFunc(texture.getDepthCompareFunc()); + // set up magnification filter + if (!texRecord.isValid() || texRecord.depthTextureCompareFunc != depthCompareFunc) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(getGLType(type), ARBShadow.GL_TEXTURE_COMPARE_FUNC_ARB, depthCompareFunc); + texRecord.depthTextureCompareFunc = depthCompareFunc; + } + } + } + + /** + * Check if the filter settings of this particular texture have been changed and apply as needed. + * + * @param texture + * our texture object + * @param texRecord + * our record of the last state of the texture in gl + * @param record + */ + public static void applyFilter(final Texture texture, final TextureRecord texRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + final Type type = texture.getType(); + + final int magFilter = LwjglTextureUtil.getGLMagFilter(texture.getMagnificationFilter()); + // set up magnification filter + if (!texRecord.isValid() || texRecord.magFilter != magFilter) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(getGLType(type), GL11.GL_TEXTURE_MAG_FILTER, magFilter); + texRecord.magFilter = magFilter; + } + + final int minFilter = LwjglTextureUtil.getGLMinFilter(texture.getMinificationFilter()); + // set up mipmap filter + if (!texRecord.isValid() || texRecord.minFilter != minFilter) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(getGLType(type), GL11.GL_TEXTURE_MIN_FILTER, minFilter); + texRecord.minFilter = minFilter; + } + + // set up aniso filter + if (caps.isAnisoSupported()) { + float aniso = texture.getAnisotropicFilterPercent() * (caps.getMaxAnisotropic() - 1.0f); + aniso += 1.0f; + if (!texRecord.isValid() || (texRecord.anisoLevel - aniso > MathUtils.ZERO_TOLERANCE)) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameterf(getGLType(type), EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso); + texRecord.anisoLevel = aniso; + } + } + } + + /** + * Check if the wrap mode of this particular texture has been changed and apply as needed. + * + * @param texture + * our texture object + * @param texRecord + * our record of the last state of the unit in gl + * @param record + */ + public static void applyWrap(final Texture3D texture, final TextureRecord texRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + if (!caps.isTexture3DSupported()) { + return; + } + + final int wrapS = getGLWrap(texture.getWrap(WrapAxis.S), caps); + final int wrapT = getGLWrap(texture.getWrap(WrapAxis.T), caps); + final int wrapR = getGLWrap(texture.getWrap(WrapAxis.R), caps); + + if (!texRecord.isValid() || texRecord.wrapS != wrapS) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_WRAP_S, wrapS); + texRecord.wrapS = wrapS; + } + if (!texRecord.isValid() || texRecord.wrapT != wrapT) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_WRAP_T, wrapT); + texRecord.wrapT = wrapT; + } + if (!texRecord.isValid() || texRecord.wrapR != wrapR) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_R, wrapR); + texRecord.wrapR = wrapR; + } + + } + + /** + * Check if the wrap mode of this particular texture has been changed and apply as needed. + * + * @param texture + * our texture object + * @param texRecord + * our record of the last state of the unit in gl + * @param record + */ + public static void applyWrap(final Texture1D texture, final TextureRecord texRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + final int wrapS = getGLWrap(texture.getWrap(WrapAxis.S), caps); + + if (!texRecord.isValid() || texRecord.wrapS != wrapS) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_WRAP_S, wrapS); + texRecord.wrapS = wrapS; + } + } + + /** + * Check if the wrap mode of this particular texture has been changed and apply as needed. + * + * @param texture + * our texture object + * @param texRecord + * our record of the last state of the unit in gl + * @param record + */ + public static void applyWrap(final Texture texture, final TextureRecord texRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + if (texture instanceof Texture2D) { + applyWrap((Texture2D) texture, texRecord, unit, record, caps); + } else if (texture instanceof Texture1D) { + applyWrap((Texture1D) texture, texRecord, unit, record, caps); + } else if (texture instanceof Texture3D) { + applyWrap((Texture3D) texture, texRecord, unit, record, caps); + } else if (texture instanceof TextureCubeMap) { + applyWrap((TextureCubeMap) texture, texRecord, unit, record, caps); + } + } + + /** + * Check if the wrap mode of this particular texture has been changed and apply as needed. + * + * @param texture + * our texture object + * @param texRecord + * our record of the last state of the unit in gl + * @param record + */ + public static void applyWrap(final Texture2D texture, final TextureRecord texRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + final int wrapS = getGLWrap(texture.getWrap(WrapAxis.S), caps); + final int wrapT = getGLWrap(texture.getWrap(WrapAxis.T), caps); + + if (!texRecord.isValid() || texRecord.wrapS != wrapS) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrapS); + texRecord.wrapS = wrapS; + } + if (!texRecord.isValid() || texRecord.wrapT != wrapT) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrapT); + texRecord.wrapT = wrapT; + } + + } + + /** + * Check if the wrap mode of this particular texture has been changed and apply as needed. + * + * @param cubeMap + * our texture object + * @param texRecord + * our record of the last state of the unit in gl + * @param record + */ + public static void applyWrap(final TextureCubeMap cubeMap, final TextureRecord texRecord, final int unit, + final TextureStateRecord record, final ContextCapabilities caps) { + if (!caps.isTextureCubeMapSupported()) { + return; + } + + final int wrapS = getGLWrap(cubeMap.getWrap(WrapAxis.S), caps); + final int wrapT = getGLWrap(cubeMap.getWrap(WrapAxis.T), caps); + final int wrapR = getGLWrap(cubeMap.getWrap(WrapAxis.R), caps); + + if (!texRecord.isValid() || texRecord.wrapS != wrapS) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_ARB, GL11.GL_TEXTURE_WRAP_S, wrapS); + texRecord.wrapS = wrapS; + } + if (!texRecord.isValid() || texRecord.wrapT != wrapT) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_ARB, GL11.GL_TEXTURE_WRAP_T, wrapT); + texRecord.wrapT = wrapT; + } + if (!texRecord.isValid() || texRecord.wrapR != wrapR) { + checkAndSetUnit(unit, record, caps); + GL11.glTexParameteri(ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_ARB, GL12.GL_TEXTURE_WRAP_R, wrapR); + texRecord.wrapR = wrapR; + } + } + + public static void deleteTexture(final Texture texture) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final TextureStateRecord record = (TextureStateRecord) context.getStateRecord(StateType.Texture); + + final Integer id = texture.getTextureIdForContextAsInteger(context.getGlContextRep()); + if (id.intValue() == 0) { + // Not on card... return. + return; + } + + final IntBuffer idBuffer = BufferUtils.createIntBuffer(1); + idBuffer.clear(); + idBuffer.put(id.intValue()); + idBuffer.rewind(); + GL11.glDeleteTextures(idBuffer); + record.removeTextureRecord(id); + texture.removeFromIdCache(context.getGlContextRep()); + } + + public static void deleteTextureIds(final Collection ids) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final TextureStateRecord record = (TextureStateRecord) context.getStateRecord(StateType.Texture); + + final IntBuffer idBuffer = BufferUtils.createIntBuffer(ids.size()); + idBuffer.clear(); + for (final Integer i : ids) { + if (i != null) { + idBuffer.put(i); + record.removeTextureRecord(i); + } + } + idBuffer.flip(); + if (idBuffer.remaining() > 0) { + GL11.glDeleteTextures(idBuffer); + } + } + + /** + * Useful for external lwjgl based classes that need to safely set the current texture. + */ + public static void doTextureBind(final Texture texture, final int unit, final boolean invalidateState) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + final TextureStateRecord record = (TextureStateRecord) context.getStateRecord(StateType.Texture); + if (invalidateState) { + // Set this to null because no current state really matches anymore + context.setCurrentState(StateType.Texture, null); + } + checkAndSetUnit(unit, record, caps); + + final int id = texture.getTextureIdForContext(context.getGlContextRep()); + GL11.glBindTexture(getGLType(texture.getType()), id); + if (Constants.stats) { + StatCollector.addStat(StatType.STAT_TEXTURE_BINDS, 1); + } + if (record != null) { + record.units[unit].boundTexture = id; + } + } + + public static int getGLType(final Type type) { + switch (type) { + case TwoDimensional: + return GL11.GL_TEXTURE_2D; + case OneDimensional: + return GL11.GL_TEXTURE_1D; + case ThreeDimensional: + return GL12.GL_TEXTURE_3D; + case CubeMap: + return ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_ARB; + } + throw new IllegalArgumentException("invalid texture type: " + type); + } + + public static int getGLCubeMapFace(final TextureCubeMap.Face face) { + switch (face) { + case PositiveX: + return ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; + case NegativeX: + return ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB; + case PositiveY: + return ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB; + case NegativeY: + return ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB; + case PositiveZ: + return ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB; + case NegativeZ: + return ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB; + } + throw new IllegalArgumentException("invalid cubemap face: " + face); + } + + public static int getGLWrap(final WrapMode wrap, final ContextCapabilities caps) { + switch (wrap) { + case Repeat: + return GL11.GL_REPEAT; + case MirroredRepeat: + if (caps.isTextureMirroredRepeatSupported()) { + return ARBTextureMirroredRepeat.GL_MIRRORED_REPEAT_ARB; + } else { + return GL11.GL_REPEAT; + } + case MirrorClamp: + if (caps.isTextureMirrorClampSupported()) { + return EXTTextureMirrorClamp.GL_MIRROR_CLAMP_EXT; + } + // FALLS THROUGH + case Clamp: + return GL11.GL_CLAMP; + case MirrorBorderClamp: + if (caps.isTextureMirrorBorderClampSupported()) { + return EXTTextureMirrorClamp.GL_MIRROR_CLAMP_TO_BORDER_EXT; + } + // FALLS THROUGH + case BorderClamp: + if (caps.isTextureBorderClampSupported()) { + return ARBTextureBorderClamp.GL_CLAMP_TO_BORDER_ARB; + } else { + return GL11.GL_CLAMP; + } + case MirrorEdgeClamp: + if (caps.isTextureMirrorEdgeClampSupported()) { + return EXTTextureMirrorClamp.GL_MIRROR_CLAMP_TO_EDGE_EXT; + } + // FALLS THROUGH + case EdgeClamp: + if (caps.isTextureEdgeClampSupported()) { + return GL12.GL_CLAMP_TO_EDGE; + } else { + return GL11.GL_CLAMP; + } + } + throw new IllegalArgumentException("invalid WrapMode type: " + wrap); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglVertexProgramStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglVertexProgramStateUtil.java new file mode 100644 index 0000000..462b0a3 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglVertexProgramStateUtil.java @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.logging.Logger; + +import org.lwjgl.opengl.ARBProgram; +import org.lwjgl.opengl.ARBVertexProgram; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.VertexProgramState; +import com.ardor3d.renderer.state.record.VertexProgramStateRecord; +import com.ardor3d.util.geom.BufferUtils; + +public abstract class LwjglVertexProgramStateUtil { + private static final Logger logger = Logger.getLogger(LwjglVertexProgramStateUtil.class.getName()); + + /** + * Queries OpenGL for errors in the vertex program. Errors are logged as SEVERE, noting both the line number and + * message. + */ + private static void checkProgramError() { + if (GL11.glGetError() == GL11.GL_INVALID_OPERATION) { + // retrieve the error position + final IntBuffer errorloc = BufferUtils.createIntBuffer(16); + GL11.glGetInteger(ARBProgram.GL_PROGRAM_ERROR_POSITION_ARB, errorloc); + + logger.severe("Error " + GL11.glGetString(ARBProgram.GL_PROGRAM_ERROR_STRING_ARB) + + " in vertex program on line " + errorloc.get(0)); + } + } + + protected static int create(final ByteBuffer program) { + + final IntBuffer buf = BufferUtils.createIntBuffer(1); + + ARBProgram.glGenProgramsARB(buf); + ARBProgram.glBindProgramARB(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB, buf.get(0)); + ARBProgram.glProgramStringARB(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB, ARBProgram.GL_PROGRAM_FORMAT_ASCII_ARB, + program); + + checkProgramError(); + + return buf.get(0); + } + + /** + * Applies this vertex program to the current scene. Checks if the GL_ARB_vertex_program extension is supported + * before attempting to enable this program. + * + * @see com.ardor3d.renderer.state.RenderState#apply() + */ + public static void apply(final VertexProgramState state) { + final RenderContext context = ContextManager.getCurrentContext(); + if (context.getCapabilities().isVertexProgramSupported()) { + // ask for the current state record + final VertexProgramStateRecord record = (VertexProgramStateRecord) context + .getStateRecord(StateType.VertexProgram); + context.setCurrentState(StateType.VertexProgram, state); + + if (!record.isValid() || record.getReference() != state) { + record.setReference(state); + if (state.isEnabled()) { + // Vertex program not yet loaded + if (state._getProgramID() == -1) { + if (state.getProgramAsBuffer() != null) { + final int id = create(state.getProgramAsBuffer()); + state._setProgramID(id); + } else { + return; + } + } + + GL11.glEnable(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB); + ARBProgram.glBindProgramARB(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB, state._getProgramID()); + + // load environmental parameters... + for (int i = 0; i < VertexProgramState._getEnvParameters().length; i++) { + if (VertexProgramState._getEnvParameters()[i] != null) { + ARBProgram.glProgramEnvParameter4fARB(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB, i, + VertexProgramState._getEnvParameters()[i][0], + VertexProgramState._getEnvParameters()[i][1], + VertexProgramState._getEnvParameters()[i][2], + VertexProgramState._getEnvParameters()[i][3]); + } + } + + // load local parameters... + if (state.isUsingParameters()) { + // no parameters are used + for (int i = 0; i < state._getParameters().length; i++) { + if (state._getParameters()[i] != null) { + ARBProgram.glProgramLocalParameter4fARB(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB, i, + state._getParameters()[i][0], state._getParameters()[i][1], + state._getParameters()[i][2], state._getParameters()[i][3]); + } + } + } + + } else { + GL11.glDisable(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB); + } + } + + if (!record.isValid()) { + record.validate(); + } + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglWireframeStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglWireframeStateUtil.java new file mode 100644 index 0000000..7ce1b24 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglWireframeStateUtil.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.lwjgl.LwjglRenderer; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.WireframeState; +import com.ardor3d.renderer.state.record.WireframeStateRecord; + +public abstract class LwjglWireframeStateUtil { + + public static void apply(final LwjglRenderer renderer, final WireframeState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final WireframeStateRecord record = (WireframeStateRecord) context.getStateRecord(StateType.Wireframe); + context.setCurrentState(StateType.Wireframe, state); + + if (state.isEnabled()) { + renderer.setupLineParameters(state.getLineWidth(), 1, (short) 0xFFFF, state.isAntialiased()); + + switch (state.getFace()) { + case Front: + applyPolyMode(GL11.GL_LINE, GL11.GL_FILL, record); + break; + case Back: + applyPolyMode(GL11.GL_FILL, GL11.GL_LINE, record); + break; + case FrontAndBack: + default: + applyPolyMode(GL11.GL_LINE, GL11.GL_LINE, record); + break; + } + } else { + applyPolyMode(GL11.GL_FILL, GL11.GL_FILL, record); + } + + if (!record.isValid()) { + record.validate(); + } + } + + private static void applyPolyMode(final int frontMode, final int backMode, final WireframeStateRecord record) { + + if (record.isValid()) { + if (frontMode == backMode && (record.frontMode != frontMode || record.backMode != backMode)) { + GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, frontMode); + record.frontMode = frontMode; + record.backMode = backMode; + } else if (frontMode != backMode) { + if (record.frontMode != frontMode) { + GL11.glPolygonMode(GL11.GL_FRONT, frontMode); + record.frontMode = frontMode; + } + if (record.backMode != backMode) { + GL11.glPolygonMode(GL11.GL_BACK, backMode); + record.backMode = backMode; + } + } + + } else { + if (frontMode == backMode) { + GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, frontMode); + } else if (frontMode != backMode) { + GL11.glPolygonMode(GL11.GL_FRONT, frontMode); + GL11.glPolygonMode(GL11.GL_BACK, backMode); + } + record.frontMode = frontMode; + record.backMode = backMode; + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglZBufferStateUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglZBufferStateUtil.java new file mode 100644 index 0000000..dbebc5c --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/LwjglZBufferStateUtil.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl; + +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.ZBufferState; +import com.ardor3d.renderer.state.record.ZBufferStateRecord; + +public abstract class LwjglZBufferStateUtil { + + public static void apply(final ZBufferState state) { + // ask for the current state record + final RenderContext context = ContextManager.getCurrentContext(); + final ZBufferStateRecord record = (ZBufferStateRecord) context.getStateRecord(StateType.ZBuffer); + context.setCurrentState(StateType.ZBuffer, state); + + enableDepthTest(state.isEnabled(), record); + if (state.isEnabled()) { + int depthFunc = 0; + switch (state.getFunction()) { + case Never: + depthFunc = GL11.GL_NEVER; + break; + case LessThan: + depthFunc = GL11.GL_LESS; + break; + case EqualTo: + depthFunc = GL11.GL_EQUAL; + break; + case LessThanOrEqualTo: + depthFunc = GL11.GL_LEQUAL; + break; + case GreaterThan: + depthFunc = GL11.GL_GREATER; + break; + case NotEqualTo: + depthFunc = GL11.GL_NOTEQUAL; + break; + case GreaterThanOrEqualTo: + depthFunc = GL11.GL_GEQUAL; + break; + case Always: + depthFunc = GL11.GL_ALWAYS; + } + applyFunction(depthFunc, record); + } + + enableWrite(state.isWritable(), record); + + if (!record.isValid()) { + record.validate(); + } + } + + private static void enableDepthTest(final boolean enable, final ZBufferStateRecord record) { + if (enable && (!record.depthTest || !record.isValid())) { + GL11.glEnable(GL11.GL_DEPTH_TEST); + record.depthTest = true; + } else if (!enable && (record.depthTest || !record.isValid())) { + GL11.glDisable(GL11.GL_DEPTH_TEST); + record.depthTest = false; + } + } + + private static void applyFunction(final int depthFunc, final ZBufferStateRecord record) { + if (depthFunc != record.depthFunc || !record.isValid()) { + GL11.glDepthFunc(depthFunc); + record.depthFunc = depthFunc; + } + } + + private static void enableWrite(final boolean enable, final ZBufferStateRecord record) { + if (enable != record.writable || !record.isValid()) { + GL11.glDepthMask(enable); + record.writable = enable; + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/shader/LwjglShaderUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/shader/LwjglShaderUtil.java new file mode 100644 index 0000000..877f2fd --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/shader/LwjglShaderUtil.java @@ -0,0 +1,400 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl.shader; + +import java.nio.ByteBuffer; +import java.util.logging.Logger; + +import org.lwjgl.opengl.ARBShaderObjects; +import org.lwjgl.opengl.ARBVertexProgram; +import org.lwjgl.opengl.ARBVertexShader; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.renderer.ContextCapabilities; +import com.ardor3d.renderer.ContextManager; +import com.ardor3d.renderer.RenderContext; +import com.ardor3d.renderer.Renderer; +import com.ardor3d.renderer.lwjgl.LwjglRenderer; +import com.ardor3d.renderer.state.RenderState.StateType; +import com.ardor3d.renderer.state.record.ShaderObjectsStateRecord; +import com.ardor3d.scene.state.lwjgl.util.LwjglRendererUtil; +import com.ardor3d.util.geom.BufferUtils; +import com.ardor3d.util.shader.ShaderVariable; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableFloat; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableFloat2; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableFloat3; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableFloat4; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableFloatArray; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableInt; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableInt2; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableInt3; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableInt4; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableIntArray; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableMatrix2; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableMatrix3; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableMatrix4; +import com.ardor3d.util.shader.uniformtypes.ShaderVariableMatrix4Array; +import com.ardor3d.util.shader.uniformtypes.ShaderVariablePointerByte; +import com.ardor3d.util.shader.uniformtypes.ShaderVariablePointerFloat; +import com.ardor3d.util.shader.uniformtypes.ShaderVariablePointerFloatMatrix; +import com.ardor3d.util.shader.uniformtypes.ShaderVariablePointerInt; +import com.ardor3d.util.shader.uniformtypes.ShaderVariablePointerShort; + +/** Utility class for updating shadervariables(uniforms and attributes) */ +public abstract class LwjglShaderUtil { + private static final Logger logger = Logger.getLogger(LwjglShaderUtil.class.getName()); + + /** + * Updates a uniform shadervariable. + * + * @param shaderVariable + * variable to update + */ + public static void updateShaderUniform(final ShaderVariable shaderVariable) { + if (!shaderVariable.hasData()) { + throw new IllegalArgumentException("shaderVariable has no data: " + shaderVariable.name + " type: " + + shaderVariable.getClass().getName()); + } + + if (shaderVariable instanceof ShaderVariableInt) { + updateShaderUniform((ShaderVariableInt) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableInt2) { + updateShaderUniform((ShaderVariableInt2) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableInt3) { + updateShaderUniform((ShaderVariableInt3) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableInt4) { + updateShaderUniform((ShaderVariableInt4) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableIntArray) { + updateShaderUniform((ShaderVariableIntArray) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableFloat) { + updateShaderUniform((ShaderVariableFloat) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableFloat2) { + updateShaderUniform((ShaderVariableFloat2) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableFloat3) { + updateShaderUniform((ShaderVariableFloat3) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableFloat4) { + updateShaderUniform((ShaderVariableFloat4) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableFloatArray) { + updateShaderUniform((ShaderVariableFloatArray) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableMatrix2) { + updateShaderUniform((ShaderVariableMatrix2) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableMatrix3) { + updateShaderUniform((ShaderVariableMatrix3) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableMatrix4) { + updateShaderUniform((ShaderVariableMatrix4) shaderVariable); + } else if (shaderVariable instanceof ShaderVariableMatrix4Array) { + updateShaderUniform((ShaderVariableMatrix4Array) shaderVariable); + } else { + logger.warning("updateShaderUniform: Unknown shaderVariable type!"); + } + } + + /** + * Update variableID for uniform shadervariable if needed. + * + * @param variable + * shadervaribale to update ID on + * @param programID + * shader program context ID + */ + public static void updateUniformLocation(final ShaderVariable variable, final int programID) { + if (variable.variableID == -1) { + final ByteBuffer nameBuf = BufferUtils.createByteBuffer(variable.name.getBytes().length + 1); + nameBuf.clear(); + nameBuf.put(variable.name.getBytes()); + nameBuf.rewind(); + + variable.variableID = ARBShaderObjects.glGetUniformLocationARB(programID, nameBuf); + + if (variable.variableID == -1 && !variable.errorLogged) { + logger.severe("Shader uniform [" + variable.name + "] could not be located in shader"); + variable.errorLogged = true; + } + } + } + + private static void updateShaderUniform(final ShaderVariableInt shaderUniform) { + ARBShaderObjects.glUniform1iARB(shaderUniform.variableID, shaderUniform.value1); + } + + private static void updateShaderUniform(final ShaderVariableInt2 shaderUniform) { + ARBShaderObjects.glUniform2iARB(shaderUniform.variableID, shaderUniform.value1, shaderUniform.value2); + } + + private static void updateShaderUniform(final ShaderVariableInt3 shaderUniform) { + ARBShaderObjects.glUniform3iARB(shaderUniform.variableID, shaderUniform.value1, shaderUniform.value2, + shaderUniform.value3); + } + + private static void updateShaderUniform(final ShaderVariableInt4 shaderUniform) { + ARBShaderObjects.glUniform4iARB(shaderUniform.variableID, shaderUniform.value1, shaderUniform.value2, + shaderUniform.value3, shaderUniform.value4); + } + + private static void updateShaderUniform(final ShaderVariableIntArray shaderUniform) { + switch (shaderUniform.size) { + case 1: + ARBShaderObjects.glUniform1ARB(shaderUniform.variableID, shaderUniform.value); + break; + case 2: + ARBShaderObjects.glUniform2ARB(shaderUniform.variableID, shaderUniform.value); + break; + case 3: + ARBShaderObjects.glUniform3ARB(shaderUniform.variableID, shaderUniform.value); + break; + case 4: + ARBShaderObjects.glUniform4ARB(shaderUniform.variableID, shaderUniform.value); + break; + default: + throw new IllegalArgumentException("Wrong size: " + shaderUniform.size); + } + } + + private static void updateShaderUniform(final ShaderVariableFloat shaderUniform) { + ARBShaderObjects.glUniform1fARB(shaderUniform.variableID, shaderUniform.value1); + } + + private static void updateShaderUniform(final ShaderVariableFloat2 shaderUniform) { + ARBShaderObjects.glUniform2fARB(shaderUniform.variableID, shaderUniform.value1, shaderUniform.value2); + } + + private static void updateShaderUniform(final ShaderVariableFloat3 shaderUniform) { + ARBShaderObjects.glUniform3fARB(shaderUniform.variableID, shaderUniform.value1, shaderUniform.value2, + shaderUniform.value3); + } + + private static void updateShaderUniform(final ShaderVariableFloat4 shaderUniform) { + ARBShaderObjects.glUniform4fARB(shaderUniform.variableID, shaderUniform.value1, shaderUniform.value2, + shaderUniform.value3, shaderUniform.value4); + } + + private static void updateShaderUniform(final ShaderVariableFloatArray shaderUniform) { + switch (shaderUniform.size) { + case 1: + ARBShaderObjects.glUniform1ARB(shaderUniform.variableID, shaderUniform.value); + break; + case 2: + ARBShaderObjects.glUniform2ARB(shaderUniform.variableID, shaderUniform.value); + break; + case 3: + ARBShaderObjects.glUniform3ARB(shaderUniform.variableID, shaderUniform.value); + break; + case 4: + ARBShaderObjects.glUniform4ARB(shaderUniform.variableID, shaderUniform.value); + break; + default: + throw new IllegalArgumentException("Wrong size: " + shaderUniform.size); + } + } + + private static void updateShaderUniform(final ShaderVariableMatrix2 shaderUniform) { + shaderUniform.matrixBuffer.rewind(); + ARBShaderObjects.glUniformMatrix2ARB(shaderUniform.variableID, shaderUniform.rowMajor, + shaderUniform.matrixBuffer); + } + + private static void updateShaderUniform(final ShaderVariableMatrix3 shaderUniform) { + shaderUniform.matrixBuffer.rewind(); + ARBShaderObjects.glUniformMatrix3ARB(shaderUniform.variableID, shaderUniform.rowMajor, + shaderUniform.matrixBuffer); + } + + private static void updateShaderUniform(final ShaderVariableMatrix4 shaderUniform) { + shaderUniform.matrixBuffer.rewind(); + ARBShaderObjects.glUniformMatrix4ARB(shaderUniform.variableID, shaderUniform.rowMajor, + shaderUniform.matrixBuffer); + } + + private static void updateShaderUniform(final ShaderVariableMatrix4Array shaderUniform) { + shaderUniform.matrixBuffer.rewind(); + ARBShaderObjects.glUniformMatrix4ARB(shaderUniform.variableID, shaderUniform.rowMajor, + shaderUniform.matrixBuffer); + } + + /** + * Update variableID for attribute shadervariable if needed. + * + * @param variable + * shadervaribale to update ID on + * @param programID + * shader program context ID + */ + public static void updateAttributeLocation(final ShaderVariable variable, final int programID) { + if (variable.variableID == -1) { + final ByteBuffer nameBuf = BufferUtils.createByteBuffer(variable.name.getBytes().length + 1); + nameBuf.clear(); + nameBuf.put(variable.name.getBytes()); + nameBuf.rewind(); + + variable.variableID = ARBVertexShader.glGetAttribLocationARB(programID, nameBuf); + + if (variable.variableID == -1 && !variable.errorLogged) { + logger.severe("Shader attribute [" + variable.name + "] could not be located in shader"); + variable.errorLogged = true; + } + } + } + + /** + * Updates an vertex attribute pointer. + * + * @param renderer + * the current renderer + * @param shaderVariable + * variable to update + * @param useVBO + * if true, we'll use VBO for the attributes, if false we'll use arrays. + */ + public static void updateShaderAttribute(final Renderer renderer, final ShaderVariable shaderVariable, + final boolean useVBO) { + if (shaderVariable.variableID == -1) { + // attribute is not bound, or was not found in shader. + return; + } + + if (!shaderVariable.hasData()) { + throw new IllegalArgumentException("shaderVariable has no data: " + shaderVariable.name + " type: " + + shaderVariable.getClass().getName()); + } + + final RenderContext context = ContextManager.getCurrentContext(); + final ContextCapabilities caps = context.getCapabilities(); + if (caps.isVBOSupported() && !useVBO) { + renderer.unbindVBO(); + } + + final ShaderObjectsStateRecord record = (ShaderObjectsStateRecord) context.getStateRecord(StateType.GLSLShader); + + if (shaderVariable instanceof ShaderVariablePointerFloat) { + updateShaderAttribute((ShaderVariablePointerFloat) shaderVariable, record, useVBO); + } else if (shaderVariable instanceof ShaderVariablePointerFloatMatrix) { + updateShaderAttribute((ShaderVariablePointerFloatMatrix) shaderVariable, record, useVBO); + } else if (shaderVariable instanceof ShaderVariablePointerByte) { + updateShaderAttribute((ShaderVariablePointerByte) shaderVariable, record, useVBO); + } else if (shaderVariable instanceof ShaderVariablePointerInt) { + updateShaderAttribute((ShaderVariablePointerInt) shaderVariable, record, useVBO); + } else if (shaderVariable instanceof ShaderVariablePointerShort) { + updateShaderAttribute((ShaderVariablePointerShort) shaderVariable, record, useVBO); + } else { + logger.warning("updateShaderAttribute: Unknown shaderVariable type!"); + return; + } + } + + public static void useShaderProgram(final int id, final ShaderObjectsStateRecord record) { + if (record.shaderId != id) { + ARBShaderObjects.glUseProgramObjectARB(id); + record.shaderId = id; + } + } + + private static void enableVertexAttribute(final ShaderVariable var, final ShaderObjectsStateRecord record) { + if (!record.enabledAttributes.contains(var)) { + if (var.getSize() == 1) { + ARBVertexProgram.glEnableVertexAttribArrayARB(var.variableID); + } else { + for (int i = 0, max = var.getSize(); i < max; i++) { + ARBVertexProgram.glEnableVertexAttribArrayARB(var.variableID + i); + } + } + record.enabledAttributes.add(var); + } + } + + private static void updateShaderAttribute(final ShaderVariablePointerFloat variable, + final ShaderObjectsStateRecord record, final boolean useVBO) { + enableVertexAttribute(variable, record); + if (useVBO) { + final RenderContext context = ContextManager.getCurrentContext(); + final int vboId = LwjglRenderer.setupVBO(variable.data, context); + LwjglRendererUtil.setBoundVBO(context.getRendererRecord(), vboId); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID, variable.size, GL11.GL_FLOAT, + variable.normalized, variable.stride, 0); + } else { + variable.data.getBuffer().rewind(); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID, variable.size, variable.normalized, + variable.stride, variable.data.getBuffer()); + } + } + + private static void updateShaderAttribute(final ShaderVariablePointerFloatMatrix variable, + final ShaderObjectsStateRecord record, final boolean useVBO) { + final int size = variable.size; + final int length = variable.data.getBuffer().capacity() / size; + final RenderContext context = ContextManager.getCurrentContext(); + int pos = 0; + enableVertexAttribute(variable, record); + for (int i = 0; i < size; i++) { + pos = (i * length); + if (useVBO) { + final int vboId = LwjglRenderer.setupVBO(variable.data, context); + LwjglRendererUtil.setBoundVBO(context.getRendererRecord(), vboId); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID + i, size, GL11.GL_FLOAT, + variable.normalized, 0, pos); + } else { + variable.data.getBuffer().limit(pos + length - 1); + variable.data.getBuffer().position(pos); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID + i, size, variable.normalized, 0, + variable.data.getBuffer()); + } + } + } + + private static void updateShaderAttribute(final ShaderVariablePointerByte variable, + final ShaderObjectsStateRecord record, final boolean useVBO) { + enableVertexAttribute(variable, record); + if (useVBO) { + final RenderContext context = ContextManager.getCurrentContext(); + final int vboId = LwjglRenderer.setupVBO(variable.data, context); + LwjglRendererUtil.setBoundVBO(context.getRendererRecord(), vboId); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID, variable.size, + variable.unsigned ? GL11.GL_UNSIGNED_BYTE : GL11.GL_BYTE, variable.normalized, variable.stride, 0); + } else { + variable.data.getBuffer().rewind(); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID, variable.size, variable.unsigned, + variable.normalized, variable.stride, variable.data.getBuffer()); + } + } + + private static void updateShaderAttribute(final ShaderVariablePointerInt variable, + final ShaderObjectsStateRecord record, final boolean useVBO) { + enableVertexAttribute(variable, record); + if (useVBO) { + final RenderContext context = ContextManager.getCurrentContext(); + final int vboId = LwjglRenderer.setupVBO(variable.data, context); + LwjglRendererUtil.setBoundVBO(context.getRendererRecord(), vboId); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID, variable.size, + variable.unsigned ? GL11.GL_UNSIGNED_INT : GL11.GL_INT, variable.normalized, variable.stride, 0); + } else { + variable.data.getBuffer().rewind(); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID, variable.size, variable.unsigned, + variable.normalized, variable.stride, variable.data.getBuffer()); + } + } + + private static void updateShaderAttribute(final ShaderVariablePointerShort variable, + final ShaderObjectsStateRecord record, final boolean useVBO) { + enableVertexAttribute(variable, record); + if (useVBO) { + final RenderContext context = ContextManager.getCurrentContext(); + final int vboId = LwjglRenderer.setupVBO(variable.data, context); + LwjglRendererUtil.setBoundVBO(context.getRendererRecord(), vboId); + ARBVertexProgram + .glVertexAttribPointerARB(variable.variableID, variable.size, + variable.unsigned ? GL11.GL_UNSIGNED_SHORT : GL11.GL_SHORT, variable.normalized, + variable.stride, 0); + } else { + variable.data.getBuffer().rewind(); + ARBVertexProgram.glVertexAttribPointerARB(variable.variableID, variable.size, variable.unsigned, + variable.normalized, variable.stride, variable.data.getBuffer()); + } + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglRendererUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglRendererUtil.java new file mode 100644 index 0000000..8abbe4d --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglRendererUtil.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl.util; + +import java.util.Stack; + +import org.lwjgl.opengl.ARBBufferObject; +import org.lwjgl.opengl.ARBVertexBufferObject; +import org.lwjgl.opengl.GL11; + +import com.ardor3d.math.Rectangle2; +import com.ardor3d.math.type.ReadOnlyRectangle2; +import com.ardor3d.renderer.state.record.RendererRecord; + +public abstract class LwjglRendererUtil { + + public static void switchMode(final RendererRecord rendRecord, final int mode) { + if (!rendRecord.isMatrixValid() || rendRecord.getMatrixMode() != mode) { + GL11.glMatrixMode(mode); + rendRecord.setMatrixMode(mode); + rendRecord.setMatrixValid(true); + } + } + + public static void setBoundVBO(final RendererRecord rendRecord, final int id) { + if (!rendRecord.isVboValid() || rendRecord.getCurrentVboId() != id) { + ARBBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, id); + rendRecord.setCurrentVboId(id); + rendRecord.setVboValid(true); + } + } + + public static void setBoundElementVBO(final RendererRecord rendRecord, final int id) { + if (!rendRecord.isElementVboValid() || rendRecord.getCurrentElementVboId() != id) { + ARBBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, id); + rendRecord.setCurrentElementVboId(id); + rendRecord.setElementVboValid(true); + } + } + + public static void applyScissors(final RendererRecord rendRecord) { + final Stack clips = rendRecord.getScissorClips(); + + if (clips.size() > 0) { + final Rectangle2 init = Rectangle2.fetchTempInstance(); + init.set(-1, -1, -1, -1); + ReadOnlyRectangle2 r; + boolean first = true; + for (int i = clips.size(); --i >= 0;) { + r = clips.get(i); + + if (r == null) { + break; + } + if (first) { + init.set(r); + first = false; + } else { + init.intersect(r, init); + } + if (init.getWidth() <= 0 || init.getHeight() <= 0) { + init.setWidth(0); + init.setHeight(0); + break; + } + } + + if (init.getWidth() == -1) { + setClippingEnabled(rendRecord, false); + } else { + setClippingEnabled(rendRecord, true); + GL11.glScissor(init.getX(), init.getY(), init.getWidth(), init.getHeight()); + } + Rectangle2.releaseTempInstance(init); + } else { + // no clips, so disable + setClippingEnabled(rendRecord, false); + } + } + + public static void setClippingEnabled(final RendererRecord rendRecord, final boolean enabled) { + if (enabled && (!rendRecord.isClippingTestValid() || !rendRecord.isClippingTestEnabled())) { + GL11.glEnable(GL11.GL_SCISSOR_TEST); + rendRecord.setClippingTestEnabled(true); + } else if (!enabled && (!rendRecord.isClippingTestValid() || rendRecord.isClippingTestEnabled())) { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + rendRecord.setClippingTestEnabled(false); + } + rendRecord.setClippingTestValid(true); + } +} diff --git a/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglTextureUtil.java b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglTextureUtil.java new file mode 100644 index 0000000..1380fe5 --- /dev/null +++ b/ardor3d-lwjgl/src/main/java/com/ardor3d/scene/state/lwjgl/util/LwjglTextureUtil.java @@ -0,0 +1,633 @@ +/** + * Copyright (c) 2008-2012 Ardor Labs, Inc. + * + * This file is part of Ardor3D. + * + * Ardor3D is free software: you can redistribute it and/or modify it + * under the terms of its license which may be found in the accompanying + * LICENSE file or at . + */ + +package com.ardor3d.scene.state.lwjgl.util; + +import org.lwjgl.opengl.ARBDepthBufferFloat; +import org.lwjgl.opengl.ARBDepthTexture; +import org.lwjgl.opengl.ARBHalfFloatPixel; +import org.lwjgl.opengl.ARBMultitexture; +import org.lwjgl.opengl.ARBShadow; +import org.lwjgl.opengl.ARBTextureCompression; +import org.lwjgl.opengl.ARBTextureEnvCombine; +import org.lwjgl.opengl.ARBTextureEnvDot3; +import org.lwjgl.opengl.ARBTextureFloat; +import org.lwjgl.opengl.ARBTextureRg; +import org.lwjgl.opengl.EXTTextureCompressionLATC; +import org.lwjgl.opengl.EXTTextureCompressionS3TC; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL30; + +import com.ardor3d.image.ImageDataFormat; +import com.ardor3d.image.PixelDataType; +import com.ardor3d.image.Texture.ApplyMode; +import com.ardor3d.image.Texture.CombinerFunctionAlpha; +import com.ardor3d.image.Texture.CombinerFunctionRGB; +import com.ardor3d.image.Texture.CombinerOperandAlpha; +import com.ardor3d.image.Texture.CombinerOperandRGB; +import com.ardor3d.image.Texture.CombinerSource; +import com.ardor3d.image.Texture.DepthTextureCompareFunc; +import com.ardor3d.image.Texture.DepthTextureCompareMode; +import com.ardor3d.image.Texture.DepthTextureMode; +import com.ardor3d.image.Texture.MagnificationFilter; +import com.ardor3d.image.Texture.MinificationFilter; +import com.ardor3d.image.TextureStoreFormat; +import com.ardor3d.renderer.state.TextureState.CorrectionType; + +public abstract class LwjglTextureUtil { + + public static int getGLInternalFormat(final TextureStoreFormat format) { + switch (format) { + // first some frequently used formats + case RGBA8: + return GL11.GL_RGBA8; + case RGB8: + return GL11.GL_RGB8; + case Alpha8: + return GL11.GL_ALPHA8; + case CompressedRGBA: + return ARBTextureCompression.GL_COMPRESSED_RGBA_ARB; + case CompressedRGB: + return ARBTextureCompression.GL_COMPRESSED_RGB_ARB; + case CompressedRG: + return GL30.GL_COMPRESSED_RG; + case CompressedRed: + return GL30.GL_COMPRESSED_RED; + case CompressedLuminance: + return ARBTextureCompression.GL_COMPRESSED_LUMINANCE_ARB; + case CompressedLuminanceAlpha: + return ARBTextureCompression.GL_COMPRESSED_LUMINANCE_ALPHA_ARB; + case NativeDXT1: + return EXTTextureCompressionS3TC.GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case NativeDXT1A: + return EXTTextureCompressionS3TC.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case NativeDXT3: + return EXTTextureCompressionS3TC.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case NativeDXT5: + return EXTTextureCompressionS3TC.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case NativeLATC_L: + return EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_LATC1_EXT; + case NativeLATC_LA: + return EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; + + // The rest... + case Alpha4: + return GL11.GL_ALPHA4; + case Alpha12: + return GL11.GL_ALPHA12; + case Alpha16: + return GL11.GL_ALPHA16; + case Luminance4: + return GL11.GL_LUMINANCE4; + case Luminance8: + return GL11.GL_LUMINANCE8; + case Luminance12: + return GL11.GL_LUMINANCE12; + case Luminance16: + return GL11.GL_LUMINANCE16; + case Intensity4: + return GL11.GL_INTENSITY4; + case Intensity8: + return GL11.GL_INTENSITY8; + case Intensity12: + return GL11.GL_INTENSITY12; + case Intensity16: + return GL11.GL_INTENSITY16; + case Luminance4Alpha4: + return GL11.GL_LUMINANCE4_ALPHA4; + case Luminance6Alpha2: + return GL11.GL_LUMINANCE6_ALPHA2; + case Luminance8Alpha8: + return GL11.GL_LUMINANCE8_ALPHA8; + case Luminance12Alpha4: + return GL11.GL_LUMINANCE12_ALPHA4; + case Luminance12Alpha12: + return GL11.GL_LUMINANCE12_ALPHA12; + case Luminance16Alpha16: + return GL11.GL_LUMINANCE16_ALPHA16; + case R3G3B2: + return GL11.GL_R3_G3_B2; + case RGB4: + return GL11.GL_RGB4; + case RGB5: + return GL11.GL_RGB5; + case RGB10: + return GL11.GL_RGB10; + case RGB12: + return GL11.GL_RGB12; + case RGB16: + return GL11.GL_RGB16; + case RGBA2: + return GL11.GL_RGBA2; + case RGBA4: + return GL11.GL_RGBA4; + case RGB5A1: + return GL11.GL_RGB5_A1; + case RGB10A2: + return GL11.GL_RGB10_A2; + case RGBA12: + return GL11.GL_RGBA12; + case RGBA16: + return GL11.GL_RGBA16; + case Depth: + return GL11.GL_DEPTH_COMPONENT; + case Depth16: + return ARBDepthTexture.GL_DEPTH_COMPONENT16_ARB; + case Depth24: + return ARBDepthTexture.GL_DEPTH_COMPONENT24_ARB; + case Depth32: + return ARBDepthTexture.GL_DEPTH_COMPONENT32_ARB; + case Depth32F: + return ARBDepthBufferFloat.GL_DEPTH_COMPONENT32F; + case RGB16F: + return ARBTextureFloat.GL_RGB16F_ARB; + case RGB32F: + return ARBTextureFloat.GL_RGB32F_ARB; + case RGBA16F: + return ARBTextureFloat.GL_RGBA16F_ARB; + case RGBA32F: + return ARBTextureFloat.GL_RGBA32F_ARB; + case Alpha16F: + return ARBTextureFloat.GL_ALPHA16F_ARB; + case Alpha32F: + return ARBTextureFloat.GL_ALPHA32F_ARB; + case Luminance16F: + return ARBTextureFloat.GL_LUMINANCE16F_ARB; + case Luminance32F: + return ARBTextureFloat.GL_LUMINANCE32F_ARB; + case LuminanceAlpha16F: + return ARBTextureFloat.GL_LUMINANCE_ALPHA16F_ARB; + case LuminanceAlpha32F: + return ARBTextureFloat.GL_LUMINANCE_ALPHA32F_ARB; + case Intensity16F: + return ARBTextureFloat.GL_INTENSITY16F_ARB; + case Intensity32F: + return ARBTextureFloat.GL_INTENSITY32F_ARB; + case R8: + return ARBTextureRg.GL_R8; + case R16: + return ARBTextureRg.GL_R16; + case RG8: + return ARBTextureRg.GL_RG8; + case RG16: + return ARBTextureRg.GL_RG16; + case R16F: + return ARBTextureRg.GL_R16F; + case R32F: + return ARBTextureRg.GL_R32F; + case RG16F: + return ARBTextureRg.GL_RG16F; + case RG32F: + return ARBTextureRg.GL_RG32F; + case R8I: + return ARBTextureRg.GL_R8I; + case R8UI: + return ARBTextureRg.GL_R8UI; + case R16I: + return ARBTextureRg.GL_R16I; + case R16UI: + return ARBTextureRg.GL_R16UI; + case R32I: + return ARBTextureRg.GL_R32I; + case R32UI: + return ARBTextureRg.GL_R32UI; + case RG8I: + return ARBTextureRg.GL_RG8I; + case RG8UI: + return ARBTextureRg.GL_RG8UI; + case RG16I: + return ARBTextureRg.GL_RG16I; + case RG16UI: + return ARBTextureRg.GL_RG16UI; + case RG32I: + return ARBTextureRg.GL_RG32I; + case RG32UI: + return ARBTextureRg.GL_RG32UI; + default: + break; + } + throw new IllegalArgumentException("Incorrect format set: " + format); + } + + public static int getGLPixelDataType(final PixelDataType type) { + switch (type) { + case Byte: + return GL11.GL_BYTE; + case Float: + return GL11.GL_FLOAT; + case HalfFloat: + return ARBHalfFloatPixel.GL_HALF_FLOAT_ARB; + case Short: + return GL11.GL_SHORT; + case UnsignedShort: + return GL11.GL_UNSIGNED_SHORT; + case Int: + return GL11.GL_INT; + case UnsignedInt: + return GL11.GL_UNSIGNED_INT; + case UnsignedByte: + return GL11.GL_UNSIGNED_BYTE; + case UnsignedByte_3_3_2: + return GL12.GL_UNSIGNED_BYTE_3_3_2; + case UnsignedByte_2_3_3_Rev: + return GL12.GL_UNSIGNED_BYTE_2_3_3_REV; + case UnsignedShort_5_6_5: + return GL12.GL_UNSIGNED_SHORT_5_6_5; + case UnsignedShort_5_6_5_Rev: + return GL12.GL_UNSIGNED_SHORT_5_6_5_REV; + case UnsignedShort_4_4_4_4: + return GL12.GL_UNSIGNED_SHORT_4_4_4_4; + case UnsignedShort_4_4_4_4_Rev: + return GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV; + case UnsignedShort_5_5_5_1: + return GL12.GL_UNSIGNED_SHORT_5_5_5_1; + case UnsignedShort_1_5_5_5_Rev: + return GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV; + case UnsignedInt_8_8_8_8: + return GL12.GL_UNSIGNED_INT_8_8_8_8; + case UnsignedInt_8_8_8_8_Rev: + return GL12.GL_UNSIGNED_INT_8_8_8_8_REV; + case UnsignedInt_10_10_10_2: + return GL12.GL_UNSIGNED_INT_10_10_10_2; + case UnsignedInt_2_10_10_10_Rev: + return GL12.GL_UNSIGNED_INT_2_10_10_10_REV; + default: + throw new Error("Unhandled type: " + type); + } + } + + public static int getGLPixelFormat(final ImageDataFormat format) { + switch (format) { + case RGBA: + return GL11.GL_RGBA; + case RGB: + return GL11.GL_RGB; + case RG: + return ARBTextureRg.GL_RG; + case Alpha: + return GL11.GL_ALPHA; + case Luminance: + return GL11.GL_LUMINANCE; + case Intensity: + return GL11.GL_INTENSITY; + case LuminanceAlpha: + return GL11.GL_LUMINANCE_ALPHA; + case Depth: + return GL11.GL_DEPTH_COMPONENT; + case BGR: + return GL12.GL_BGR; + case BGRA: + return GL12.GL_BGRA; + case Red: + return GL11.GL_RED; + case Blue: + return GL11.GL_BLUE; + case Green: + return GL11.GL_GREEN; + case ColorIndex: + return GL11.GL_COLOR_INDEX; + case StencilIndex: + return GL11.GL_STENCIL_INDEX; + default: + break; + } + throw new IllegalArgumentException("Incorrect format set: " + format); + } + + public static int getGLPixelFormatFromStoreFormat(final TextureStoreFormat format) { + switch (format) { + case RGBA2: + case RGBA4: + case RGBA8: + case RGB5A1: + case RGB10A2: + case RGBA12: + case RGBA16: + case CompressedRGBA: + case NativeDXT1A: + case NativeDXT3: + case NativeDXT5: + case RGBA16F: + case RGBA32F: + return GL11.GL_RGBA; + case R3G3B2: + case RGB4: + case RGB5: + case RGB8: + case RGB10: + case RGB12: + case RGB16: + case CompressedRGB: + case NativeDXT1: + case RGB16F: + case RGB32F: + return GL11.GL_RGB; + case Alpha4: + case Alpha8: + case Alpha12: + case Alpha16: + case Alpha16F: + case Alpha32F: + return GL11.GL_ALPHA; + case Luminance4: + case Luminance8: + case Luminance12: + case Luminance16: + case Luminance16F: + case Luminance32F: + case CompressedLuminance: + case NativeLATC_L: + return GL11.GL_LUMINANCE; + case Intensity4: + case Intensity8: + case Intensity12: + case Intensity16: + case Intensity16F: + case Intensity32F: + return GL11.GL_INTENSITY; + case Luminance4Alpha4: + case Luminance6Alpha2: + case Luminance8Alpha8: + case Luminance12Alpha4: + case Luminance12Alpha12: + case Luminance16Alpha16: + case LuminanceAlpha16F: + case LuminanceAlpha32F: + case CompressedLuminanceAlpha: + case NativeLATC_LA: + return GL11.GL_LUMINANCE_ALPHA; + case Depth: + case Depth16: + case Depth24: + case Depth32: + case Depth32F: + return GL11.GL_DEPTH_COMPONENT; + case R8: + case R16: + case R16F: + case R32F: + case R8I: + case R8UI: + case R16I: + case R16UI: + case R32I: + case R32UI: + case CompressedRed: + return ARBTextureRg.GL_RED; + case RG8: + case RG16: + case RG16F: + case RG32F: + case RG8I: + case RG8UI: + case RG16I: + case RG16UI: + case RG32I: + case RG32UI: + case CompressedRG: + return ARBTextureRg.GL_RG; + default: + break; + } + throw new IllegalArgumentException("Incorrect format set: " + format); + } + + public static int getGLDepthTextureMode(final DepthTextureMode mode) { + switch (mode) { + case Alpha: + return GL11.GL_ALPHA; + case Luminance: + return GL11.GL_LUMINANCE; + case Intensity: + default: + return GL11.GL_INTENSITY; + } + } + + public static int getGLDepthTextureCompareMode(final DepthTextureCompareMode mode) { + switch (mode) { + case RtoTexture: + return ARBShadow.GL_COMPARE_R_TO_TEXTURE_ARB; + case None: + default: + return GL11.GL_NONE; + } + } + + public static int getGLDepthTextureCompareFunc(final DepthTextureCompareFunc func) { + switch (func) { + case GreaterThanEqual: + return GL11.GL_GEQUAL; + case LessThanEqual: + default: + return GL11.GL_LEQUAL; + } + } + + public static int getGLMagFilter(final MagnificationFilter magFilter) { + switch (magFilter) { + case Bilinear: + return GL11.GL_LINEAR; + case NearestNeighbor: + default: + return GL11.GL_NEAREST; + + } + } + + public static int getGLMinFilter(final MinificationFilter filter) { + switch (filter) { + case BilinearNoMipMaps: + return GL11.GL_LINEAR; + case Trilinear: + return GL11.GL_LINEAR_MIPMAP_LINEAR; + case BilinearNearestMipMap: + return GL11.GL_LINEAR_MIPMAP_NEAREST; + case NearestNeighborNoMipMaps: + return GL11.GL_NEAREST; + case NearestNeighborNearestMipMap: + return GL11.GL_NEAREST_MIPMAP_NEAREST; + case NearestNeighborLinearMipMap: + return GL11.GL_NEAREST_MIPMAP_LINEAR; + } + throw new IllegalArgumentException("invalid MinificationFilter type: " + filter); + } + + public static int getGLEnvMode(final ApplyMode apply) { + switch (apply) { + case Replace: + return GL11.GL_REPLACE; + case Blend: + return GL11.GL_BLEND; + case Combine: + return ARBTextureEnvCombine.GL_COMBINE_ARB; + case Decal: + return GL11.GL_DECAL; + case Add: + return GL11.GL_ADD; + case Modulate: + return GL11.GL_MODULATE; + } + throw new IllegalArgumentException("invalid ApplyMode type: " + apply); + } + + public static int getPerspHint(final CorrectionType type) { + switch (type) { + case Perspective: + return GL11.GL_NICEST; + case Affine: + return GL11.GL_FASTEST; + } + throw new IllegalArgumentException("unknown correction type: " + type); + } + + public static int getGLCombineOpRGB(final CombinerOperandRGB operand) { + switch (operand) { + case SourceColor: + return GL11.GL_SRC_COLOR; + case OneMinusSourceColor: + return GL11.GL_ONE_MINUS_SRC_COLOR; + case SourceAlpha: + return GL11.GL_SRC_ALPHA; + case OneMinusSourceAlpha: + return GL11.GL_ONE_MINUS_SRC_ALPHA; + } + throw new IllegalArgumentException("invalid CombinerOperandRGB type: " + operand); + } + + public static int getGLCombineOpAlpha(final CombinerOperandAlpha operand) { + switch (operand) { + case SourceAlpha: + return GL11.GL_SRC_ALPHA; + case OneMinusSourceAlpha: + return GL11.GL_ONE_MINUS_SRC_ALPHA; + } + throw new IllegalArgumentException("invalid CombinerOperandAlpha type: " + operand); + } + + public static int getGLCombineSrc(final CombinerSource combineSrc) { + switch (combineSrc) { + case CurrentTexture: + return GL11.GL_TEXTURE; + case PrimaryColor: + return ARBTextureEnvCombine.GL_PRIMARY_COLOR_ARB; + case Constant: + return ARBTextureEnvCombine.GL_CONSTANT_ARB; + case Previous: + return ARBTextureEnvCombine.GL_PREVIOUS_ARB; + case TextureUnit0: + return ARBMultitexture.GL_TEXTURE0_ARB; + case TextureUnit1: + return ARBMultitexture.GL_TEXTURE1_ARB; + case TextureUnit2: + return ARBMultitexture.GL_TEXTURE2_ARB; + case TextureUnit3: + return ARBMultitexture.GL_TEXTURE3_ARB; + case TextureUnit4: + return ARBMultitexture.GL_TEXTURE4_ARB; + case TextureUnit5: + return ARBMultitexture.GL_TEXTURE5_ARB; + case TextureUnit6: + return ARBMultitexture.GL_TEXTURE6_ARB; + case TextureUnit7: + return ARBMultitexture.GL_TEXTURE7_ARB; + case TextureUnit8: + return ARBMultitexture.GL_TEXTURE8_ARB; + case TextureUnit9: + return ARBMultitexture.GL_TEXTURE9_ARB; + case TextureUnit10: + return ARBMultitexture.GL_TEXTURE10_ARB; + case TextureUnit11: + return ARBMultitexture.GL_TEXTURE11_ARB; + case TextureUnit12: + return ARBMultitexture.GL_TEXTURE12_ARB; + case TextureUnit13: + return ARBMultitexture.GL_TEXTURE13_ARB; + case TextureUnit14: + return ARBMultitexture.GL_TEXTURE14_ARB; + case TextureUnit15: + return ARBMultitexture.GL_TEXTURE15_ARB; + case TextureUnit16: + return ARBMultitexture.GL_TEXTURE16_ARB; + case TextureUnit17: + return ARBMultitexture.GL_TEXTURE17_ARB; + case TextureUnit18: + return ARBMultitexture.GL_TEXTURE18_ARB; + case TextureUnit19: + return ARBMultitexture.GL_TEXTURE19_ARB; + case TextureUnit20: + return ARBMultitexture.GL_TEXTURE20_ARB; + case TextureUnit21: + return ARBMultitexture.GL_TEXTURE21_ARB; + case TextureUnit22: + return ARBMultitexture.GL_TEXTURE22_ARB; + case TextureUnit23: + return ARBMultitexture.GL_TEXTURE23_ARB; + case TextureUnit24: + return ARBMultitexture.GL_TEXTURE24_ARB; + case TextureUnit25: + return ARBMultitexture.GL_TEXTURE25_ARB; + case TextureUnit26: + return ARBMultitexture.GL_TEXTURE26_ARB; + case TextureUnit27: + return ARBMultitexture.GL_TEXTURE27_ARB; + case TextureUnit28: + return ARBMultitexture.GL_TEXTURE28_ARB; + case TextureUnit29: + return ARBMultitexture.GL_TEXTURE29_ARB; + case TextureUnit30: + return ARBMultitexture.GL_TEXTURE30_ARB; + case TextureUnit31: + return ARBMultitexture.GL_TEXTURE31_ARB; + } + throw new IllegalArgumentException("invalid CombinerSource type: " + combineSrc); + } + + public static int getGLCombineFuncAlpha(final CombinerFunctionAlpha combineFunc) { + switch (combineFunc) { + case Modulate: + return GL11.GL_MODULATE; + case Replace: + return GL11.GL_REPLACE; + case Add: + return GL11.GL_ADD; + case AddSigned: + return ARBTextureEnvCombine.GL_ADD_SIGNED_ARB; + case Subtract: + return ARBTextureEnvCombine.GL_SUBTRACT_ARB; + case Interpolate: + return ARBTextureEnvCombine.GL_INTERPOLATE_ARB; + } + throw new IllegalArgumentException("invalid CombinerFunctionAlpha type: " + combineFunc); + } + + public static int getGLCombineFuncRGB(final CombinerFunctionRGB combineFunc) { + switch (combineFunc) { + case Modulate: + return GL11.GL_MODULATE; + case Replace: + return GL11.GL_REPLACE; + case Add: + return GL11.GL_ADD; + case AddSigned: + return ARBTextureEnvCombine.GL_ADD_SIGNED_ARB; + case Subtract: + return ARBTextureEnvCombine.GL_SUBTRACT_ARB; + case Interpolate: + return ARBTextureEnvCombine.GL_INTERPOLATE_ARB; + case Dot3RGB: + return ARBTextureEnvDot3.GL_DOT3_RGB_ARB; + case Dot3RGBA: + return ARBTextureEnvDot3.GL_DOT3_RGBA_ARB; + } + throw new IllegalArgumentException("invalid CombinerFunctionRGB type: " + combineFunc); + } +} diff --git a/ardor3d-lwjgl/src/main/native/linux/libjinput-linux.so b/ardor3d-lwjgl/src/main/native/linux/libjinput-linux.so new file mode 100644 index 0000000..b6f1090 Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/linux/libjinput-linux.so differ diff --git a/ardor3d-lwjgl/src/main/native/linux/libjinput-linux64.so b/ardor3d-lwjgl/src/main/native/linux/libjinput-linux64.so new file mode 100644 index 0000000..5fb7265 Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/linux/libjinput-linux64.so differ diff --git a/ardor3d-lwjgl/src/main/native/linux/liblwjgl.so b/ardor3d-lwjgl/src/main/native/linux/liblwjgl.so new file mode 100644 index 0000000..b5271dd Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/linux/liblwjgl.so differ diff --git a/ardor3d-lwjgl/src/main/native/linux/liblwjgl64.so b/ardor3d-lwjgl/src/main/native/linux/liblwjgl64.so new file mode 100644 index 0000000..2ba3daa Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/linux/liblwjgl64.so differ diff --git a/ardor3d-lwjgl/src/main/native/linux/libopenal.so b/ardor3d-lwjgl/src/main/native/linux/libopenal.so new file mode 100644 index 0000000..2b5a8d5 Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/linux/libopenal.so differ diff --git a/ardor3d-lwjgl/src/main/native/linux/libopenal64.so b/ardor3d-lwjgl/src/main/native/linux/libopenal64.so new file mode 100644 index 0000000..ce1c38f Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/linux/libopenal64.so differ diff --git a/ardor3d-lwjgl/src/main/native/macosx/libjinput-osx.jnilib b/ardor3d-lwjgl/src/main/native/macosx/libjinput-osx.jnilib new file mode 100644 index 0000000..51c089c Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/macosx/libjinput-osx.jnilib differ diff --git a/ardor3d-lwjgl/src/main/native/macosx/liblwjgl.jnilib b/ardor3d-lwjgl/src/main/native/macosx/liblwjgl.jnilib new file mode 100644 index 0000000..8d8c446 Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/macosx/liblwjgl.jnilib differ diff --git a/ardor3d-lwjgl/src/main/native/solaris/liblwjgl.so b/ardor3d-lwjgl/src/main/native/solaris/liblwjgl.so new file mode 100644 index 0000000..d8155d1 Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/solaris/liblwjgl.so differ diff --git a/ardor3d-lwjgl/src/main/native/solaris/libopenal.so b/ardor3d-lwjgl/src/main/native/solaris/libopenal.so new file mode 100644 index 0000000..50edb3e Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/solaris/libopenal.so differ diff --git a/ardor3d-lwjgl/src/main/native/win32/OpenAL32.dll b/ardor3d-lwjgl/src/main/native/win32/OpenAL32.dll new file mode 100644 index 0000000..8dc9fa5 Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/win32/OpenAL32.dll differ diff --git a/ardor3d-lwjgl/src/main/native/win32/jinput-dx8.dll b/ardor3d-lwjgl/src/main/native/win32/jinput-dx8.dll new file mode 100644 index 0000000..0ad824b Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/win32/jinput-dx8.dll differ diff --git a/ardor3d-lwjgl/src/main/native/win32/jinput-raw.dll b/ardor3d-lwjgl/src/main/native/win32/jinput-raw.dll new file mode 100644 index 0000000..2a014f6 Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/win32/jinput-raw.dll differ diff --git a/ardor3d-lwjgl/src/main/native/win32/lwjgl.dll b/ardor3d-lwjgl/src/main/native/win32/lwjgl.dll new file mode 100644 index 0000000..387c60d Binary files /dev/null and b/ardor3d-lwjgl/src/main/native/win32/lwjgl.dll differ -- cgit v1.2.3