com.ardor3d.renderer.lwjgl.LwjglPbufferTextureRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.ardor3d.renderer.lwjgl.LwjglPbufferTextureRenderer.java

Source

/**
 * 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 <http://www.ardor3d.com/LICENSE>.
 */

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;

/**
 * <p>
 * This class is used by Ardor3D's LWJGL implementation to render textures. Users should <b>not </b> create this class
 * directly.
 * </p>
 * 
 * @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);
    }

    /**
     * <code>setupTexture</code> 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<? extends Spatial> 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<? extends Spatial> 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<Texture> texs, final int clear) {
        render(null, spat, null, texs, clear);
    }

    public void render(final List<? extends Spatial> spat, final List<Texture> texs, final int clear) {
        render(spat, null, null, texs, clear);
    }

    public void render(final Scene scene, final List<Texture> texs, final int clear) {
        render(null, null, scene, texs, clear);
    }

    private void render(final List<? extends Spatial> toDrawA, final Spatial toDrawB, final Scene toDrawC,
            final List<Texture> 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;
            }
        }
    }
}