All Downloads are FREE. Search and download functionalities are using the official Maven repository.

box2dLight.RayHandler Maven / Gradle / Ivy

There is a newer version: 1.5
Show newest version
package box2dLight;

import shaders.LightShader;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;

/**
 * Handler that manages everything related to lights updating and rendering
 * 

Implements {@link Disposable} * @author kalle_h */ public class RayHandler implements Disposable { /** Gamma correction value used if enabled * TODO: remove final modifier and provide method to change * this default value if needed to anyone? */ static final float GAMMA_COR = 0.625f; static boolean gammaCorrection = false; static float gammaCorrectionParameter = 1f; /** if this is public why we have a setter? * TODO: remove public modifier and add getter * */ public static boolean isDiffuse = false; /** * Blend function for lights rendering with both shadows and diffusion *

Default: (GL20.GL_DST_COLOR, GL20.GL_ZERO) */ public final BlendFunc diffuseBlendFunc = new BlendFunc(GL20.GL_DST_COLOR, GL20.GL_ZERO); /** * Blend function for lights rendering with shadows but without diffusion *

Default: (GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA) */ public final BlendFunc shadowBlendFunc = new BlendFunc(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA); /** * Blend function for lights rendering without shadows and diffusion *

Default: (GL20.GL_SRC_ALPHA, GL20.GL_ONE) */ public final BlendFunc simpleBlendFunc = new BlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE); final Matrix4 combined = new Matrix4(); final Color ambientLight = new Color(); /** * This Array contain all the lights. * *

NOTE: DO NOT MODIFY THIS LIST */ final Array lightList = new Array(false, 16); /** * This Array contain all the disabled lights. * *

NOTE: DO NOT MODIFY THIS LIST */ final Array disabledLights = new Array(false, 16); LightMap lightMap; final ShaderProgram lightShader; ShaderProgram customLightShader = null; boolean culling = true; boolean shadows = true; boolean blur = true; int blurNum = 1; boolean customViewport = false; int viewportX = 0; int viewportY = 0; int viewportWidth = Gdx.graphics.getWidth(); int viewportHeight = Gdx.graphics.getHeight(); /** How many lights passed culling and rendered to scene last time */ int lightRenderedLastFrame = 0; /** camera matrix corners */ float x1, x2, y1, y2; World world; /** * Class constructor specifying the physics world from where collision * geometry is taken. * *

NOTE: FBO size is 1/4 * screen size and used by default. * *

    Default setting are: *
  • culling = true *
  • shadows = true *
  • diffuse = false *
  • blur = true *
  • blurNum = 1 *
  • ambientLight = 0f *
* * @see #RayHandler(World, int, int) */ public RayHandler(World world) { this(world, Gdx.graphics.getWidth() / 4, Gdx.graphics .getHeight() / 4); } /** * Class constructor specifying the physics world from where collision * geometry is taken, and size of FBO used for intermediate rendering. * * @see #RayHandler(World) */ public RayHandler(World world, int fboWidth, int fboHeight) { this.world = world; resizeFBO(fboWidth, fboHeight); lightShader = LightShader.createLightShader(); } /** * Resize the FBO used for intermediate rendering. */ public void resizeFBO(int fboWidth, int fboHeight) { if (lightMap != null) { lightMap.dispose(); } lightMap = new LightMap(this, fboWidth, fboHeight); } /** * Sets combined matrix basing on camera position, rotation and zoom * *

Same as calling: * {@code setCombinedMatrix( * camera.combined, * camera.position.x, * camera.position.y, * camera.viewportWidth * camera.zoom, * camera.viewportHeight * camera.zoom );} * * @see #setCombinedMatrix(Matrix4, float, float, float, float) */ public void setCombinedMatrix(OrthographicCamera camera) { this.setCombinedMatrix( camera.combined, camera.position.x, camera.position.y, camera.viewportWidth * camera.zoom, camera.viewportHeight * camera.zoom); } /** * Sets combined camera matrix. * *

Matrix must be set to work in box2d coordinates, it will be copied * and used for culling and rendering. Remember to update it if camera * changes. This will work with rotated cameras. * *

NOTE: Matrix4 is assumed to be orthogonal for culling * and directional lights. * * @param combined * matrix that include projection and translation matrices * * @deprecated use {@link #setCombinedMatrix(OrthographicCamera)} or * {@link #setCombinedMatrix(Matrix4, float, float, float, float)} instead */ @Deprecated public void setCombinedMatrix(Matrix4 combined) { System.arraycopy(combined.val, 0, this.combined.val, 0, 16); // updateCameraCorners float invWidth = combined.val[Matrix4.M00]; final float halfViewPortWidth = 1f / invWidth; final float x = -halfViewPortWidth * combined.val[Matrix4.M03]; x1 = x - halfViewPortWidth; x2 = x + halfViewPortWidth; float invHeight = combined.val[Matrix4.M11]; final float halfViewPortHeight = 1f / invHeight; final float y = -halfViewPortHeight * combined.val[Matrix4.M13]; y1 = y - halfViewPortHeight; y2 = y + halfViewPortHeight; } /** * Sets combined camera matrix. * *

Matrix must be set to work in box2d coordinates, it will be copied * and used for culling and rendering. Remember to update it if camera * changes. This will work with rotated cameras. * * @param combined * matrix that include projection and translation matrices * @param x * combined matrix position * @param y * combined matrix position * @param viewPortWidth * NOTE!! use actual size, remember to multiple with zoom value * if pulled from OrthoCamera * @param viewPortHeight * NOTE!! use actual size, remember to multiple with zoom value * if pulled from OrthoCamera * * @see #setCombinedMatrix(OrthographicCamera) */ public void setCombinedMatrix(Matrix4 combined, float x, float y, float viewPortWidth, float viewPortHeight) { System.arraycopy(combined.val, 0, this.combined.val, 0, 16); // updateCameraCorners final float halfViewPortWidth = viewPortWidth * 0.5f; x1 = x - halfViewPortWidth; x2 = x + halfViewPortWidth; final float halfViewPortHeight = viewPortHeight * 0.5f; y1 = y - halfViewPortHeight; y2 = y + halfViewPortHeight; } /** * Utility method to check if light is on the screen * @param x - light center x-coord * @param y - light center y-coord * @param radius - maximal light distance * * @return true if camera screen intersects or contains provided * light, represented by circle/box area */ boolean intersect(float x, float y, float radius) { return (x1 < (x + radius) && x2 > (x - radius) && y1 < (y + radius) && y2 > (y - radius)); } /** * Updates and renders all active lights. * *

NOTE! Remember to set combined matrix before this method. * *

Don't call this inside of any begin/end statements. * Call this method after you have rendered background but before UI. * Box2d bodies can be rendered before or after depending how you want * the x-ray lights to interact with them. * * @see #update() * @see #render() */ public void updateAndRender() { update(); render(); } /** * Manual update method for all active lights. * *

Use this if you have less physics steps than rendering steps. * * @see #updateAndRender() * @see #render() */ public void update() { for (Light light : lightList) { light.update(); } } /** * Manual rendering method for all lights. * *

NOTE! Remember to set combined matrix and update lights * before using this method manually. * *

Don't call this inside of any begin/end statements. * Call this method after you have rendered background but before UI. * Box2d bodies can be rendered before or after depending how you want * the x-ray lights to interact with them. * * @see #updateAndRender() * @see #update() * @see #setCombinedMatrix(Matrix4) * @see #setCombinedMatrix(Matrix4, float, float, float, float) */ public void render() { lightRenderedLastFrame = 0; Gdx.gl.glDepthMask(false); Gdx.gl.glEnable(GL20.GL_BLEND); simpleBlendFunc.apply(); boolean useLightMap = (shadows || blur); if (useLightMap) { lightMap.frameBuffer.begin(); Gdx.gl.glClearColor(0f, 0f, 0f, 0f); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); } ShaderProgram shader = customLightShader != null ? customLightShader : lightShader; shader.begin(); { shader.setUniformMatrix("u_projTrans", combined); if (customLightShader != null) updateLightShader(); for (Light light : lightList) { if (customLightShader != null) updateLightShaderPerLight(light); light.render(); } } shader.end(); if (useLightMap) { if (customViewport) { lightMap.frameBuffer.end( viewportX, viewportY, viewportWidth, viewportHeight); } else { lightMap.frameBuffer.end(); } lightMap.render(); } } /** * Called before light rendering start * * Override this if you are using custom light shader */ protected void updateLightShader () { } /** * Called for custom light shader before each light is rendered * * Override this if you are using custom light shader */ protected void updateLightShaderPerLight (Light light) { } /** * Checks whether the given point is inside of any light volume * * @return true if point is inside of any light volume */ public boolean pointAtLight(float x, float y) { for (Light light : lightList) { if (light.contains(x, y)) return true; } return false; } /** * Checks whether the given point is outside of all light volumes * * @return true if point is NOT inside of any light volume */ public boolean pointAtShadow(float x, float y) { for (Light light : lightList) { if (light.contains(x, y)) return false; } return true; } /** * Disposes all this rayHandler lights and resources */ public void dispose() { removeAll(); if (lightMap != null) lightMap.dispose(); if (lightShader != null) lightShader.dispose(); } /** * Removes and disposes both all active and disabled lights */ public void removeAll() { for (Light light : lightList) { light.dispose(); } lightList.clear(); for (Light light : disabledLights) { light.dispose(); } disabledLights.clear(); } /** * Set custom light shader, null to reset to default * * Changes will take effect next time #render() is called */ public void setLightShader (ShaderProgram customLightShader) { this.customLightShader = customLightShader; } /** * Enables/disables culling. * *

This save CPU and GPU time when the world is bigger than the screen. * *

Default = true */ public void setCulling(boolean culling) { this.culling = culling; } /** * Enables/disables Gaussian blur. * *

This make lights much more softer and realistic look but cost some * precious shader time. With default FBO size on android cost around 1ms. * *

Default = true * * @see #setBlurNum(int) */ public void setBlur(boolean blur) { this.blur = blur; } /** * Sets number of Gaussian blur passes. * *

Blurring can be pretty heavy weight operation, 1-3 should be safe. * Setting this to 0 is the same as disabling it. * *

Default = 1 * * @see #setBlur(boolean) */ public void setBlurNum(int blurNum) { this.blurNum = blurNum; } /** * Enables/disables shadows */ public void setShadows(boolean shadows) { this.shadows = shadows; } /** * Sets ambient light brightness. Specifies shadows brightness. *

Default = 0 * * @param ambientLight * shadows brightness value, clamped to [0f; 1f] * * @see #setAmbientLight(Color) * @see #setAmbientLight(float, float, float, float) */ public void setAmbientLight(float ambientLight) { this.ambientLight.a = MathUtils.clamp(ambientLight, 0f, 1f); } /** * Sets ambient light color. * Specifies how shadows colored and their brightness. * *

Default = Color(0, 0, 0, 0) * * @param r * shadows color red component * @param g * shadows color green component * @param b * shadows color blue component * @param a * shadows brightness component * * @see #setAmbientLight(float) * @see #setAmbientLight(Color) */ public void setAmbientLight(float r, float g, float b, float a) { this.ambientLight.set(r, g, b, a); } /** * Sets ambient light color. * Specifies how shadows colored and their brightness. * *

Default = Color(0, 0, 0, 0) * * @param ambientLightColor * color whose RGB components specify the shadows coloring and * alpha specify shadows brightness * * @see #setAmbientLight(float) * @see #setAmbientLight(float, float, float, float) */ public void setAmbientLight(Color ambientLightColor) { this.ambientLight.set(ambientLightColor); } /** * Sets physics world to work with for this rayHandler */ public void setWorld(World world) { this.world = world; } /** * @return if gamma correction is enabled or not */ public static boolean getGammaCorrection() { return gammaCorrection; } /** * Enables/disables gamma correction. * *

This need to be done before creating instance of rayHandler. * *

NOTE: To match the visuals with gamma uncorrected lights the light * distance parameters is modified implicitly. */ public static void setGammaCorrection(boolean gammaCorrectionWanted) { gammaCorrection = gammaCorrectionWanted; gammaCorrectionParameter = gammaCorrection ? GAMMA_COR : 1f; } /** * Enables/disables usage of diffuse algorithm. * *

If set to true lights are blended using the diffuse shader. This is * more realistic model than normally used as it preserve colors but might * look bit darker and also it might improve performance slightly. */ public static void useDiffuseLight(boolean useDiffuse) { isDiffuse = useDiffuse; } /** * Sets rendering to custom viewport with specified position and size *

Note: you will be responsible for update of viewport via this method * in case of any changes (on resize) */ public void useCustomViewport(int x, int y, int width, int height) { customViewport = true; viewportX = x; viewportY = y; viewportWidth = width; viewportHeight = height; } /** * Sets rendering to default viewport * *

0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight() */ public void useDefaultViewport() { customViewport = false; } /** * Enables/disables lightMap automatic rendering. * *

If set to false user needs to use the {@link #getLightMapTexture()} * and render that or use it as a light map when rendering. Example shader * for spriteBatch is given. This is faster way to do if there is not that * much overdrawing or if just couple object need light/shadows. * *

Default = true */ public void setLightMapRendering(boolean isAutomatic) { lightMap.lightMapDrawingDisabled = !isAutomatic; } /** * Expert functionality * * @return Texture that contain lightmap texture that can be used as light * texture in your shaders */ public Texture getLightMapTexture() { return lightMap.frameBuffer.getColorBufferTexture(); } /** * Expert functionality, no support given * * @return FrameBuffer that contains lightMap */ public FrameBuffer getLightMapBuffer() { return lightMap.frameBuffer; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy