Java tutorial
/** ** This file is part of the project https://github.com/toss-dev/VoxelEngine ** ** License is available here: https://raw.githubusercontent.com/toss-dev/VoxelEngine/master/LICENSE.md ** ** PEREIRA Romain ** 4-----7 ** /| /| ** 0-----3 | ** | 5___|_6 ** |/ | / ** 1-----2 */ package com.grillecube.engine.renderer.world; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL14; import org.lwjgl.opengl.GL30; import com.grillecube.engine.Logger; import com.grillecube.engine.Taskable; import com.grillecube.engine.VoxelEngine; import com.grillecube.engine.maths.Vector3f; import com.grillecube.engine.maths.Vector4f; import com.grillecube.engine.opengl.GLH; import com.grillecube.engine.opengl.object.GLFrameBuffer; import com.grillecube.engine.opengl.object.GLRenderBuffer; import com.grillecube.engine.opengl.object.GLTexture; import com.grillecube.engine.renderer.MainRenderer; import com.grillecube.engine.renderer.Renderer; import com.grillecube.engine.renderer.camera.CameraPerspectiveWorld; import com.grillecube.engine.renderer.camera.CameraProjectiveWorld; import com.grillecube.engine.renderer.model.ModelRenderer; import com.grillecube.engine.renderer.world.lines.LineRenderer; import com.grillecube.engine.renderer.world.particles.ParticleRenderer; import com.grillecube.engine.renderer.world.sky.SkyRenderer; import com.grillecube.engine.renderer.world.terrain.TerrainRenderer; import com.grillecube.engine.sound.ALH; import com.grillecube.engine.world.World; public class WorldRenderer extends Renderer { public static final int REFLECTION_FBO_WIDTH = 640; public static final int REFLECTION_FBO_HEIGHT = (int) (REFLECTION_FBO_WIDTH / 1.6f); public static final int REFRACTION_FBO_WIDTH = 500; public static final int REFRACTION_FBO_HEIGHT = (int) (REFRACTION_FBO_WIDTH / 1.6f); public static final int SHADOW_FBO_WIDTH = 2048; // the bigger the better public static final int SHADOW_FBO_HEIGHT = SHADOW_FBO_WIDTH; // reflection fbo private GLFrameBuffer _reflection_fbo; private GLTexture _reflection_texture; private GLRenderBuffer _reflection_depth_buffer; // refraction fbo private GLFrameBuffer _refraction_fbo; private GLTexture _refraction_texture; private GLRenderBuffer _refraction_depth_buffer; // shadow fbo private GLFrameBuffer _shadow_fbo; private GLTexture _shadow_map; /** a clipping plane that does not clip anything */ public static final Vector4f NO_CLIPPING = new Vector4f(0, 0, 0, 0); /** sky renderer */ private SkyRenderer _sky_renderer; /** line renderer */ private LineRenderer _line_renderer; /** main terrain renderer */ private TerrainRenderer _terrain_renderer; /** model view projection renderer */ private MVPRenderer _mvp_renderer; /** model renderer */ private ModelRenderer _model_renderer; /** particles renderer */ private ParticleRenderer _particle_renderer; /** shadows */ private ShadowCamera _shadow_camera; /** world to render */ private World _world; /** renderers */ private ArrayList<RendererWorld> _renderers; public WorldRenderer(MainRenderer main_renderer) { super(main_renderer); } @Override public void initialize() { Logger.get().log(Logger.Level.DEBUG, "Initializing " + this.getClass().getSimpleName()); this._renderers = new ArrayList<RendererWorld>(); this._sky_renderer = new SkyRenderer(this.getParent()); this._line_renderer = new LineRenderer(this.getParent()); this._terrain_renderer = new TerrainRenderer(this.getParent()); this._model_renderer = new ModelRenderer(this.getParent()); this._mvp_renderer = new MVPRenderer(this.getParent()); this._particle_renderer = new ParticleRenderer(this.getParent()); this._renderers.add(this._sky_renderer); this._renderers.add(this._line_renderer); this._renderers.add(this._terrain_renderer); this._renderers.add(this._model_renderer); this._renderers.add(this._mvp_renderer); this._renderers.add(this._particle_renderer); this.createReflectionFBO(); this.createRefractionFBO(); this.createShadowFBO(); this._shadow_camera = new ShadowCamera(this.getParent().getGLFWWindow()); for (RendererWorld renderer : this._renderers) { Logger.get().log(Logger.Level.DEBUG, "Initializing " + renderer.getClass().getSimpleName()); renderer.initialize(); } } @Override public void deinitialize() { Logger.get().log(Logger.Level.DEBUG, "Deinitializing " + this.getClass().getSimpleName()); this.deleteReflectionFBO(); this.deleteRefractionFBO(); this.deleteShadowFBO(); for (RendererWorld renderer : this._renderers) { Logger.get().log(Logger.Level.DEBUG, "Deinitializing " + renderer.getClass().getSimpleName()); renderer.deinitialize(); } } private void onWorldSet(World world) { for (RendererWorld renderer : this._renderers) { renderer.onWorldSet(world); } } private void onWorldUnset(World world) { for (RendererWorld renderer : this._renderers) { renderer.onWorldUnset(world); } } /** add a renderer to be render every frames */ public void addRenderer(RendererWorld renderer) { this._renderers.remove(this._particle_renderer); this._renderers.add(renderer); this._renderers.add(this._particle_renderer); } @Override public void preRender() { // pre render every renderer for (RendererWorld renderer : this._renderers) { renderer.preRender(); GLH.glhCheckError("post " + renderer.getClass().getSimpleName() + ".preRender()"); } // // update reflection and refraction fbos // //TODO : put a config and only render reflection / refraction if // needed // this.renderReflectionRefraction(); // // // update shadow map // //TODO : put a config and only render shadow if needed // this.renderShadows(); if (GLH.glhGetContext().getWindow().isKeyPressed(GLFW.GLFW_KEY_C)) { this.getParent().getResourceManager().getSoundManager().playSoundAt( ALH.alhLoadSound("C:/Users/Romain/AppData/Roaming/VoxelEngine/assets/POT/sounds/acoustic1.wav"), this.getCamera().getPosition(), new Vector3f(0, 0, 0)); } } @Override public void postRender() { for (RendererWorld renderer : this._renderers) { renderer.postRender(); GLH.glhCheckError("post " + renderer.getClass().getSimpleName() + ".postRender()"); } } /** render the given world */ @Override public void render() { // README : commented stuff are used to debug renderer. To see different // performances GLH.glhCheckError("pre world renderer"); // long total = 0; // long times[] = new long[this._renderers.size()]; // // final world renderer for (int i = 0; i < this._renderers.size(); i++) { RendererWorld renderer = this._renderers.get(i); // // long time = System.nanoTime(); renderer.render(); // long difft = System.nanoTime() - time; // times[i] = difft; // total += difft; // GLH.glhCheckError("post " + renderer.getClass().getSimpleName() + ".render()"); } // float ftotal = 0; // for (int i = 0 ; i < this._renderers.size() ; i++) { // // RendererWorld renderer = this._renderers.get(i); // float ftime = times[i] / (float)total; // ftotal += ftime; // Logger.get().log(Logger.Level.DEBUG, // renderer.getClass().getSimpleName(), ftime); // } // Logger.get().log(Logger.Level.DEBUG, "/" + ftotal); // Logger.get().log(Logger.Level.DEBUG, " "); } /** render reflection and refraction to the main renderer fbos */ private Vector4f _clipplane = new Vector4f(); /** render reflection and refraction planes to their given fbos */ public void renderReflectionRefraction() { // save current camera CameraProjectiveWorld realcamera = this.getCamera(); float liquid_height = realcamera.getLiquidHeight(); // float liquid_height = realcamera.getLiquidHeight(); // REFLECTION STARTS HERE // copy of the currentcamera CameraProjectiveWorld camera = (CameraProjectiveWorld) realcamera.clone(); float distance = 2 * (camera.getPosition().y - liquid_height); camera.getPosition().y -= distance; camera.invertPitch(); camera.invertRoll(); camera.update(); this.getReflectionFBO().bind(); this.getReflectionFBO().clear(); this.getReflectionFBO().viewport(0, 0, REFLECTION_FBO_WIDTH, REFLECTION_FBO_HEIGHT); this._clipplane.set(0, 1, 0, -liquid_height); for (RendererWorld renderer : this._renderers) { renderer.renderReflection(camera, this._clipplane); GLH.glhCheckError("post " + renderer.getClass().getSimpleName() + ".renderReflection()"); } this.getReflectionFBO().unbind(); // REFRACTION STARTS HERE this.getRefractionFBO().bind(); this.getRefractionFBO().clear(); this.getRefractionFBO().viewport(0, 0, REFRACTION_FBO_WIDTH, REFRACTION_FBO_HEIGHT); this._clipplane.set(0, -1, 0, liquid_height); for (RendererWorld renderer : this._renderers) { renderer.renderRefraction(realcamera, this._clipplane); GLH.glhCheckError("post " + renderer.getClass().getSimpleName() + ".renderRefraction()"); } this.getRefractionFBO().unbind(); } private void renderShadows() { if (!(this.getCamera() instanceof CameraPerspectiveWorld)) { return; } this._shadow_camera.update(this.getWorld(), this.getCamera()); this.getShadowMapFBO().bind(); this.getShadowMapFBO().clear(); this.getShadowMapFBO().viewport(0, 0, SHADOW_FBO_WIDTH, SHADOW_FBO_HEIGHT); // render shadows for (RendererWorld renderer : this._renderers) { renderer.renderShadow(this._shadow_camera); GLH.glhCheckError("post " + renderer.getClass().getSimpleName() + ".renderShadow(ShadowBox shadowbox)"); } this.getShadowMapFBO().unbind(); } public void setWorld(World world) { World prevworld = this._world; this._world = world; // if we are setting a world and none was set before if (prevworld == null && world != null) { // initialize renderers this.initialize(); this.onWorldSet(world); // else if we are changing the world } else if (prevworld != null && world != null) { this.onWorldUnset(prevworld); this.onWorldSet(world); // else if we are setting a null world but one was set before } else if (prevworld != null && world == null) { this.onWorldUnset(prevworld); this.deinitialize(); } } public World getWorld() { return (this._world); } /** return the default particle renderer */ public ParticleRenderer getParticleRenderer() { return (this._particle_renderer); } public MVPRenderer getMVPRenderer() { return (this._mvp_renderer); } public CameraProjectiveWorld getCamera() { return (this.getParent().getCamera()); } public TerrainRenderer getTerrainRenderer() { return (this._terrain_renderer); } public LineRenderer getLineRenderer() { return (this._line_renderer); } private void createReflectionFBO() { this._reflection_fbo = GLH.glhGenFBO(); this._reflection_fbo.bind(); this._reflection_fbo.createDrawBuffer(GL30.GL_COLOR_ATTACHMENT0); this._reflection_texture = GLH.glhGenTexture(); this._reflection_texture.bind(GL11.GL_TEXTURE_2D); this._reflection_texture.bind(GL13.GL_TEXTURE0, GL11.GL_TEXTURE_2D); this._reflection_texture.image2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, REFLECTION_FBO_WIDTH, REFLECTION_FBO_HEIGHT, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); this._reflection_texture.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); this._reflection_texture.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); this._reflection_fbo.createTextureAttachment(this._reflection_texture, GL30.GL_COLOR_ATTACHMENT0); this._reflection_depth_buffer = this._reflection_fbo.createRenderBuffer(REFLECTION_FBO_WIDTH, REFLECTION_FBO_HEIGHT, GL30.GL_DEPTH_ATTACHMENT); this._reflection_fbo.unbind(); } private void deleteReflectionFBO() { GLH.glhDeleteObject(this._reflection_fbo); GLH.glhDeleteObject(this._reflection_texture); GLH.glhDeleteObject(this._reflection_depth_buffer); } private void createRefractionFBO() { this._refraction_fbo = GLH.glhGenFBO(); this._refraction_fbo.bind(); this._refraction_fbo.createDrawBuffer(GL30.GL_COLOR_ATTACHMENT0); this._refraction_texture = GLH.glhGenTexture(); this._refraction_texture.bind(GL11.GL_TEXTURE_2D); this._refraction_texture.bind(GL13.GL_TEXTURE0, GL11.GL_TEXTURE_2D); this._refraction_texture.image2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, REFRACTION_FBO_WIDTH, REFRACTION_FBO_HEIGHT, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); this._refraction_texture.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); this._refraction_texture.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); this._refraction_fbo.createTextureAttachment(this._refraction_texture, GL30.GL_COLOR_ATTACHMENT0); this._refraction_depth_buffer = this._reflection_fbo.createRenderBuffer(REFRACTION_FBO_WIDTH, REFRACTION_FBO_HEIGHT, GL30.GL_DEPTH_ATTACHMENT); this._refraction_fbo.unbind(); } private void deleteRefractionFBO() { GLH.glhDeleteObject(this._refraction_fbo); GLH.glhDeleteObject(this._refraction_texture); GLH.glhDeleteObject(this._refraction_depth_buffer); } private void createShadowFBO() { this._shadow_fbo = GLH.glhGenFBO(); this._shadow_fbo.bind(); this._shadow_fbo.createDrawBuffer(GL11.GL_NONE); this._shadow_map = GLH.glhGenTexture(); this._shadow_map.bind(GL11.GL_TEXTURE_2D); this._shadow_map.image2D(GL11.GL_TEXTURE_2D, 0, GL14.GL_DEPTH_COMPONENT16, SHADOW_FBO_WIDTH, SHADOW_FBO_HEIGHT, 0, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, (ByteBuffer) null); this._shadow_map.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); this._shadow_map.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); this._shadow_map.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE); this._shadow_map.parameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE); this._shadow_fbo.createTextureAttachment(this._shadow_map, GL30.GL_DEPTH_ATTACHMENT); this._shadow_fbo.unbind(); } private void deleteShadowFBO() { GLH.glhDeleteObject(this._shadow_fbo); GLH.glhDeleteObject(this._shadow_map); } public GLFrameBuffer getReflectionFBO() { return (this._reflection_fbo); } public GLFrameBuffer getRefractionFBO() { return (this._refraction_fbo); } public GLFrameBuffer getShadowMapFBO() { return (this._shadow_fbo); } public GLTexture getReflectionTexture() { return (this._reflection_texture); } public GLTexture getRefractionTexture() { return (this._refraction_texture); } public GLTexture getShadowMap() { return (this._shadow_map); } public ShadowCamera getShadowCamera() { return (this._shadow_camera); } public Collection<RendererWorld> getRenderers() { return (this._renderers); } @Override public void getTasks(VoxelEngine engine, ArrayList<VoxelEngine.Callable<Taskable>> tasks) { World world = this.getWorld(); CameraProjectiveWorld camera = this.getCamera(); if (world == null || camera == null) { return; } for (RendererWorld renderer : this.getRenderers()) { renderer.getTasks(engine, tasks, world, camera); } } /** request the world renderer to update reflection/refraciton textures */ public void requestReflectionRefractionUpdate() { // TODO } }