/******************************************************************************* * Copyright 2019 metaphore * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.crashinvaders.vfx.effects; import com.badlogic.gdx.Application; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Pixmap; import com.crashinvaders.vfx.VfxRenderContext; import com.crashinvaders.vfx.effects.util.CopyEffect; import com.crashinvaders.vfx.effects.util.MixEffect; import com.crashinvaders.vfx.framebuffer.VfxPingPongWrapper; import com.crashinvaders.vfx.framebuffer.VfxFrameBuffer; import com.crashinvaders.vfx.framebuffer.VfxFrameBufferQueue; /** A motion blur effect which draws the last frame with a lower opacity. * The result is then stored as the next last frame to create the trail effect. */ public class MotionBlurEffect extends CompositeVfxEffect implements ChainVfxEffect { private final MixEffect mixFilter; private final CopyEffect copyFilter; private final VfxFrameBufferQueue localBuffer; private boolean firstFrameRendered = false; public MotionBlurEffect(Pixmap.Format pixelFormat, MixEffect.Method mixMethod, float blurFactor) { mixFilter = register(new MixEffect(mixMethod)); mixFilter.setMixFactor(blurFactor); copyFilter = register(new CopyEffect()); localBuffer = new VfxFrameBufferQueue(pixelFormat, // On WebGL (GWT) we cannot render from/into the same texture simultaneously. // Will use ping-pong approach to avoid "writing into itself". Gdx.app.getType() == Application.ApplicationType.WebGL ? 2 : 1 ); } @Override public void dispose() { super.dispose(); localBuffer.dispose(); } @Override public void resize(int width, int height) { super.resize(width, height); localBuffer.resize(width, height); firstFrameRendered = false; } @Override public void rebind() { super.rebind(); localBuffer.rebind(); } @Override public void render(VfxRenderContext context, VfxPingPongWrapper buffers) { VfxFrameBuffer prevFrame = this.localBuffer.changeToNext(); if (!firstFrameRendered) { // Mix filter requires two frames to render, so we gonna skip the first call. copyFilter.render(context, buffers.getSrcBuffer(), prevFrame); buffers.swap(); firstFrameRendered = true; return; } mixFilter.render(context, buffers.getSrcBuffer(), prevFrame, buffers.getDstBuffer()); copyFilter.render(context, buffers.getDstBuffer(), prevFrame); } }