Package com.base.engine.rendering

Source Code of com.base.engine.rendering.Shader

/*
* Copyright (C) 2014 Benny Bobaganoosh
*
* 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.base.engine.rendering;

import com.base.engine.components.BaseLight;
import com.base.engine.components.DirectionalLight;
import com.base.engine.components.PointLight;
import com.base.engine.components.SpotLight;
import com.base.engine.core.*;
import com.base.engine.rendering.resourceManagement.ShaderResource;

import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL32.*;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;

public class Shader
{
  private static HashMap<String, ShaderResource> s_loadedShaders = new HashMap<String, ShaderResource>();

  private ShaderResource m_resource;
  private String         m_fileName;

  public Shader(String fileName)
  {
    this.m_fileName = fileName;

    ShaderResource oldResource = s_loadedShaders.get(fileName);

    if(oldResource != null)
    {
      m_resource = oldResource;
      m_resource.AddReference();
    }
    else
    {
      m_resource = new ShaderResource();

      String vertexShaderText = LoadShader(fileName + ".vs");
      String fragmentShaderText = LoadShader(fileName + ".fs");

      AddVertexShader(vertexShaderText);
      AddFragmentShader(fragmentShaderText);

      AddAllAttributes(vertexShaderText);

      CompileShader();

      AddAllUniforms(vertexShaderText);
      AddAllUniforms(fragmentShaderText);
     
      s_loadedShaders.put(fileName, m_resource);
    }
  }

  @Override
  protected void finalize()
  {
    if(m_resource.RemoveReference() && !m_fileName.isEmpty())
    {
      s_loadedShaders.remove(m_fileName);
    }
  }

  public void Bind()
  {
    glUseProgram(m_resource.GetProgram());
  }

  public void UpdateUniforms(Transform transform, Material material, RenderingEngine renderingEngine)
  {
    Matrix4f worldMatrix = transform.GetTransformation();
    Matrix4f MVPMatrix = renderingEngine.GetMainCamera().GetViewProjection().Mul(worldMatrix);

    for(int i = 0; i < m_resource.GetUniformNames().size(); i++)
    {
      String uniformName = m_resource.GetUniformNames().get(i);
      String uniformType = m_resource.GetUniformTypes().get(i);

      if(uniformType.equals("sampler2D"))
      {
        int samplerSlot = renderingEngine.GetSamplerSlot(uniformName);
        material.GetTexture(uniformName).Bind(samplerSlot);
        SetUniformi(uniformName, samplerSlot);
      }
      else if(uniformName.startsWith("T_"))
      {
        if(uniformName.equals("T_MVP"))
          SetUniform(uniformName, MVPMatrix);
        else if(uniformName.equals("T_model"))
          SetUniform(uniformName, worldMatrix);
        else
          throw new IllegalArgumentException(uniformName + " is not a valid component of Transform");
      }
      else if(uniformName.startsWith("R_"))
      {
        String unprefixedUniformName = uniformName.substring(2);
        if(uniformType.equals("vec3"))
          SetUniform(uniformName, renderingEngine.GetVector3f(unprefixedUniformName));
        else if(uniformType.equals("float"))
          SetUniformf(uniformName, renderingEngine.GetFloat(unprefixedUniformName));
        else if(uniformType.equals("DirectionalLight"))
          SetUniformDirectionalLight(uniformName, (DirectionalLight) renderingEngine.GetActiveLight());
        else if(uniformType.equals("PointLight"))
          SetUniformPointLight(uniformName, (PointLight) renderingEngine.GetActiveLight());
        else if(uniformType.equals("SpotLight"))
          SetUniformSpotLight(uniformName, (SpotLight) renderingEngine.GetActiveLight());
        else
          renderingEngine.UpdateUniformStruct(transform, material, this, uniformName, uniformType);
      }
      else if(uniformName.startsWith("C_"))
      {
        if(uniformName.equals("C_eyePos"))
          SetUniform(uniformName, renderingEngine.GetMainCamera().GetTransform().GetTransformedPos());
        else
          throw new IllegalArgumentException(uniformName + " is not a valid component of Camera");
      }
      else
      {
        if(uniformType.equals("vec3"))
          SetUniform(uniformName, material.GetVector3f(uniformName));
        else if(uniformType.equals("float"))
          SetUniformf(uniformName, material.GetFloat(uniformName));
        else
          throw new IllegalArgumentException(uniformType + " is not a supported type in Material");
      }
    }
  }

  private void AddAllAttributes(String shaderText)
  {
    final String ATTRIBUTE_KEYWORD = "attribute";
    int attributeStartLocation = shaderText.indexOf(ATTRIBUTE_KEYWORD);
    int attribNumber = 0;
    while(attributeStartLocation != -1)
    {
      if(!(attributeStartLocation != 0
        && (Character.isWhitespace(shaderText.charAt(attributeStartLocation - 1)) || shaderText.charAt(attributeStartLocation - 1) == ';')
        && Character.isWhitespace(shaderText.charAt(attributeStartLocation + ATTRIBUTE_KEYWORD.length())))) {
          attributeStartLocation = shaderText.indexOf(ATTRIBUTE_KEYWORD, attributeStartLocation + ATTRIBUTE_KEYWORD.length());
          continue;
         
      }

      int begin = attributeStartLocation + ATTRIBUTE_KEYWORD.length() + 1;
      int end = shaderText.indexOf(";", begin);

      String attributeLine = shaderText.substring(begin, end).trim();
      String attributeName = attributeLine.substring(attributeLine.indexOf(' ') + 1, attributeLine.length()).trim();

      SetAttribLocation(attributeName, attribNumber);
      attribNumber++;

      attributeStartLocation = shaderText.indexOf(ATTRIBUTE_KEYWORD, attributeStartLocation + ATTRIBUTE_KEYWORD.length());
    }
  }

  private class GLSLStruct
  {
    public String name;
    public String type;
  }

  private HashMap<String, ArrayList<GLSLStruct>> FindUniformStructs(String shaderText)
  {
    HashMap<String, ArrayList<GLSLStruct>> result = new HashMap<String, ArrayList<GLSLStruct>>();

    final String STRUCT_KEYWORD = "struct";
    int structStartLocation = shaderText.indexOf(STRUCT_KEYWORD);
    while(structStartLocation != -1)
    {
      if(!(structStartLocation != 0
          && (Character.isWhitespace(shaderText.charAt(structStartLocation - 1)) || shaderText.charAt(structStartLocation - 1) == ';')
          && Character.isWhitespace(shaderText.charAt(structStartLocation + STRUCT_KEYWORD.length())))) {
        structStartLocation = shaderText.indexOf(STRUCT_KEYWORD, structStartLocation + STRUCT_KEYWORD.length());
        continue;
      }

      int nameBegin = structStartLocation + STRUCT_KEYWORD.length() + 1;
      int braceBegin = shaderText.indexOf("{", nameBegin);
      int braceEnd = shaderText.indexOf("}", braceBegin);

      String structName = shaderText.substring(nameBegin, braceBegin).trim();
      ArrayList<GLSLStruct> glslStructs = new ArrayList<GLSLStruct>();

      int componentSemicolonPos = shaderText.indexOf(";", braceBegin);
      while(componentSemicolonPos != -1 && componentSemicolonPos < braceEnd)
      {
        int componentNameEnd = componentSemicolonPos + 1;

        while(Character.isWhitespace(shaderText.charAt(componentNameEnd - 1)) || shaderText.charAt(componentNameEnd - 1) == ';')
          componentNameEnd--;

        int componentNameStart = componentSemicolonPos;

        while(!Character.isWhitespace(shaderText.charAt(componentNameStart - 1)))
          componentNameStart--;

        int componentTypeEnd = componentNameStart;

        while(Character.isWhitespace(shaderText.charAt(componentTypeEnd - 1)))
          componentTypeEnd--;

        int componentTypeStart = componentTypeEnd;

        while(!Character.isWhitespace(shaderText.charAt(componentTypeStart - 1)))
          componentTypeStart--;

        String componentName = shaderText.substring(componentNameStart, componentNameEnd);
        String componentType = shaderText.substring(componentTypeStart, componentTypeEnd);

        GLSLStruct glslStruct = new GLSLStruct();
        glslStruct.name = componentName;
        glslStruct.type = componentType;

        glslStructs.add(glslStruct);

        componentSemicolonPos = shaderText.indexOf(";", componentSemicolonPos + 1);
      }

      result.put(structName, glslStructs);

      structStartLocation = shaderText.indexOf(STRUCT_KEYWORD, structStartLocation + STRUCT_KEYWORD.length());
    }

    return result;
  }

  private void AddAllUniforms(String shaderText)
  {
    HashMap<String, ArrayList<GLSLStruct>> structs = FindUniformStructs(shaderText);

    final String UNIFORM_KEYWORD = "uniform";
    int uniformStartLocation = shaderText.indexOf(UNIFORM_KEYWORD);
    while(uniformStartLocation != -1)
    {
      if(!(uniformStartLocation != 0
          && (Character.isWhitespace(shaderText.charAt(uniformStartLocation - 1)) || shaderText.charAt(uniformStartLocation - 1) == ';')
          && Character.isWhitespace(shaderText.charAt(uniformStartLocation + UNIFORM_KEYWORD.length())))) {
        uniformStartLocation = shaderText.indexOf(UNIFORM_KEYWORD, uniformStartLocation + UNIFORM_KEYWORD.length());
        continue;
      }

      int begin = uniformStartLocation + UNIFORM_KEYWORD.length() + 1;
      int end = shaderText.indexOf(";", begin);

      String uniformLine = shaderText.substring(begin, end).trim();

      int whiteSpacePos = uniformLine.indexOf(' ');
      String uniformName = uniformLine.substring(whiteSpacePos + 1, uniformLine.length()).trim();
      String uniformType = uniformLine.substring(0, whiteSpacePos).trim();

      m_resource.GetUniformNames().add(uniformName);
      m_resource.GetUniformTypes().add(uniformType);
      AddUniform(uniformName, uniformType, structs);

      uniformStartLocation = shaderText.indexOf(UNIFORM_KEYWORD, uniformStartLocation + UNIFORM_KEYWORD.length());
    }
  }

  private void AddUniform(String uniformName, String uniformType, HashMap<String, ArrayList<GLSLStruct>> structs)
  {
    boolean addThis = true;
    ArrayList<GLSLStruct> structComponents = structs.get(uniformType);

    if(structComponents != null)
    {
      addThis = false;
      for(GLSLStruct struct : structComponents)
      {
        AddUniform(uniformName + "." + struct.name, struct.type, structs);
      }
    }

    if(!addThis)
      return;

    int uniformLocation = glGetUniformLocation(m_resource.GetProgram(), uniformName);

    if(uniformLocation == 0xFFFFFFFF)
    {
      System.err.println("Error: Could not find uniform: " + uniformName);
      new Exception().printStackTrace();
      System.exit(1);
    }

    m_resource.GetUniforms().put(uniformName, uniformLocation);
  }

  private void AddVertexShader(String text)
  {
    AddProgram(text, GL_VERTEX_SHADER);
  }

  private void AddGeometryShader(String text)
  {
    AddProgram(text, GL_GEOMETRY_SHADER);
  }

  private void AddFragmentShader(String text)
  {
    AddProgram(text, GL_FRAGMENT_SHADER);
  }

  private void SetAttribLocation(String attributeName, int location)
  {
    glBindAttribLocation(m_resource.GetProgram(), location, attributeName);
  }

  private void CompileShader()
  {
    glLinkProgram(m_resource.GetProgram());
   
    if(glGetProgrami(m_resource.GetProgram(), GL_LINK_STATUS) == 0)
    {
      System.err.println(glGetProgramInfoLog(m_resource.GetProgram(), 1024));
      System.exit(1);
    }
   
    glValidateProgram(m_resource.GetProgram());
   
    if(glGetProgrami(m_resource.GetProgram(), GL_VALIDATE_STATUS) == 0)
    {
      System.err.println(glGetProgramInfoLog(m_resource.GetProgram(), 1024));
      System.exit(1);
    }
  }

  private void AddProgram(String text, int type)
  {
    int shader = glCreateShader(type);
   
    if(shader == 0)
    {
      System.err.println("Shader creation failed: Could not find valid memory location when adding shader");
      System.exit(1);
    }
   
    glShaderSource(shader, text);
    glCompileShader(shader);
   
    if(glGetShaderi(shader, GL_COMPILE_STATUS) == 0)
    {
      System.err.println(glGetShaderInfoLog(shader, 1024));
      System.exit(1);
    }
   
    glAttachShader(m_resource.GetProgram(), shader);
  }

  private static String LoadShader(String fileName)
  {
    StringBuilder shaderSource = new StringBuilder();
    BufferedReader shaderReader = null;
    final String INCLUDE_DIRECTIVE = "#include";

    try
    {
      shaderReader = new BufferedReader(new FileReader("./res/shaders/" + fileName));
      String line;

      while((line = shaderReader.readLine()) != null)
      {
        if(line.startsWith(INCLUDE_DIRECTIVE))
        {
          shaderSource.append(LoadShader(line.substring(INCLUDE_DIRECTIVE.length() + 2, line.length() - 1)));
        }
        else
          shaderSource.append(line).append("\n");
      }
     
      shaderReader.close();
    }
    catch(Exception e)
    {
      e.printStackTrace();
      System.exit(1);
    }
   
   
    return shaderSource.toString();
  }
 
  public void SetUniformi(String uniformName, int value)
  {
    glUniform1i(m_resource.GetUniforms().get(uniformName), value);
  }
 
  public void SetUniformf(String uniformName, float value)
  {
    glUniform1f(m_resource.GetUniforms().get(uniformName), value);
  }
 
  public void SetUniform(String uniformName, Vector3f value)
  {
    glUniform3f(m_resource.GetUniforms().get(uniformName), value.GetX(), value.GetY(), value.GetZ());
  }
 
  public void SetUniform(String uniformName, Matrix4f value)
  {
    glUniformMatrix4(m_resource.GetUniforms().get(uniformName), true, Util.CreateFlippedBuffer(value));
  }

  public void SetUniformBaseLight(String uniformName, BaseLight baseLight)
  {
    SetUniform(uniformName + ".color", baseLight.GetColor());
    SetUniformf(uniformName + ".intensity", baseLight.GetIntensity());
  }

  public void SetUniformDirectionalLight(String uniformName, DirectionalLight directionalLight)
  {
    SetUniformBaseLight(uniformName + ".base", directionalLight);
    SetUniform(uniformName + ".direction", directionalLight.GetDirection());
  }

  public void SetUniformPointLight(String uniformName, PointLight pointLight)
  {
    SetUniformBaseLight(uniformName + ".base", pointLight);
    SetUniformf(uniformName + ".atten.constant", pointLight.GetAttenuation().GetConstant());
    SetUniformf(uniformName + ".atten.linear", pointLight.GetAttenuation().GetLinear());
    SetUniformf(uniformName + ".atten.exponent", pointLight.GetAttenuation().GetExponent());
    SetUniform(uniformName + ".position", pointLight.GetTransform().GetTransformedPos());
    SetUniformf(uniformName + ".range", pointLight.GetRange());
  }

  public void SetUniformSpotLight(String uniformName, SpotLight spotLight)
  {
    SetUniformPointLight(uniformName + ".pointLight", spotLight);
    SetUniform(uniformName + ".direction", spotLight.GetDirection());
    SetUniformf(uniformName + ".cutoff", spotLight.GetCutoff());
  }
}
TOP

Related Classes of com.base.engine.rendering.Shader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.