org.terasology.rendering.dag.nodes.DirectionalLightsNode.java Source code

Java tutorial

Introduction

Here is the source code for org.terasology.rendering.dag.nodes.DirectionalLightsNode.java

Source

/*
 * Copyright 2016 MovingBlocks
 *
 * 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 org.terasology.rendering.dag.nodes;

import org.lwjgl.opengl.GL13;
import org.terasology.assets.ResourceUrn;
import org.terasology.math.geom.Vector3f;
import org.terasology.monitoring.PerformanceMonitor;
import org.terasology.registry.In;
import org.terasology.rendering.assets.material.Material;
import org.terasology.rendering.backdrop.BackdropProvider;
import org.terasology.rendering.cameras.Camera;
import org.terasology.rendering.dag.AbstractNode;
import org.terasology.rendering.logic.LightComponent;
import static org.terasology.rendering.opengl.DefaultDynamicFBOs.READ_ONLY_GBUFFER;
import static org.terasology.rendering.opengl.DefaultDynamicFBOs.WRITE_ONLY_GBUFFER;
import org.terasology.rendering.opengl.FBO;
import org.terasology.rendering.opengl.FBOConfig;
import static org.terasology.rendering.opengl.ScalingFactors.FULL_SCALE;
import org.terasology.rendering.opengl.fbms.DisplayResolutionDependentFBOs;
import org.terasology.rendering.world.WorldRenderer;
import static org.terasology.rendering.opengl.OpenGLUtils.bindDisplay;
import static org.terasology.rendering.opengl.OpenGLUtils.renderFullscreenQuad;
import static org.terasology.rendering.opengl.OpenGLUtils.setViewportToSizeOf;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;

/**
 * TODO: Break this node into several nodes
 * TODO: For doing that worldRenderer.renderLightComponent must be eliminated somehow
 */
public class DirectionalLightsNode extends AbstractNode {
    public static final ResourceUrn REFRACTIVE_REFLECTIVE = new ResourceUrn("engine:sceneReflectiveRefractive");

    @In
    private BackdropProvider backdropProvider;

    @In
    private WorldRenderer worldRenderer;

    @In
    private DisplayResolutionDependentFBOs displayResolutionDependentFBOs;

    // TODO: Review this? (What are we doing with a component not attached to an entity?)
    private LightComponent mainDirectionalLight = new LightComponent();

    private Camera playerCamera;

    private Material lightGeometryShader;
    private Material lightBufferPass;

    private FBO sceneReflectiveRefractive;

    @Override
    public void initialise() {
        playerCamera = worldRenderer.getActiveCamera();
        lightGeometryShader = worldRenderer.getMaterial("engine:prog.lightGeometryPass");
        lightBufferPass = worldRenderer.getMaterial("engine:prog.lightBufferPass");
        requiresFBO(new FBOConfig(REFRACTIVE_REFLECTIVE, FULL_SCALE, FBO.Type.HDR).useNormalBuffer(),
                displayResolutionDependentFBOs);

        initMainDirectionalLight();
    }

    // TODO: one day the main light (sun/moon) should be just another light in the scene.
    private void initMainDirectionalLight() {
        mainDirectionalLight.lightType = LightComponent.LightType.DIRECTIONAL;
        mainDirectionalLight.lightAmbientIntensity = 0.75f;
        mainDirectionalLight.lightDiffuseIntensity = 0.75f;
        mainDirectionalLight.lightSpecularPower = 100f;
    }

    @Override
    public void process() {
        PerformanceMonitor.startActivity("rendering/directionallights");
        READ_ONLY_GBUFFER.bind();

        Vector3f sunlightWorldPosition = new Vector3f(backdropProvider.getSunDirection(true));
        sunlightWorldPosition.scale(50000f);
        sunlightWorldPosition.add(playerCamera.getPosition());
        // TODO: find a more elegant way
        // TODO: iterating over RenderSystems for rendering multiple lights
        worldRenderer.renderLightComponent(mainDirectionalLight, sunlightWorldPosition, lightGeometryShader, false);

        // TODO: Investigate these might be redundant
        glDisable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glEnable(GL_DEPTH_TEST);

        READ_ONLY_GBUFFER.setRenderBufferMask(true, true, true);
        bindDisplay();

        applyLightBufferPass();
        PerformanceMonitor.endActivity();
    }

    /**
     * Part of the deferred lighting technique, this method applies lighting through screen-space
     * calculations to the previously flat-lit world rendering stored in the primary FBO.   // TODO: rename sceneOpaque* FBOs to primaryA/B
     * <p>
     * See http://en.wikipedia.org/wiki/Deferred_shading as a starting point.
     */
    private void applyLightBufferPass() {
        int texId = 0;

        GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
        READ_ONLY_GBUFFER.bindTexture();
        lightBufferPass.setInt("texSceneOpaque", texId++, true);

        GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
        READ_ONLY_GBUFFER.bindDepthTexture();
        lightBufferPass.setInt("texSceneOpaqueDepth", texId++, true);

        GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
        READ_ONLY_GBUFFER.bindNormalsTexture();
        lightBufferPass.setInt("texSceneOpaqueNormals", texId++, true);

        GL13.glActiveTexture(GL13.GL_TEXTURE0 + texId);
        READ_ONLY_GBUFFER.bindLightBufferTexture();
        lightBufferPass.setInt("texSceneOpaqueLightBuffer", texId, true);

        sceneReflectiveRefractive = displayResolutionDependentFBOs.get(REFRACTIVE_REFLECTIVE);

        WRITE_ONLY_GBUFFER.bind();
        WRITE_ONLY_GBUFFER.setRenderBufferMask(true, true, true);

        setViewportToSizeOf(WRITE_ONLY_GBUFFER);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // TODO: verify this is necessary

        renderFullscreenQuad();

        bindDisplay(); // TODO: verify this is necessary
        setViewportToSizeOf(READ_ONLY_GBUFFER); // TODO: verify this is necessary

        displayResolutionDependentFBOs.swapReadWriteBuffers();
        READ_ONLY_GBUFFER.attachDepthBufferTo(sceneReflectiveRefractive);
    }
}