Liqua

2019-03-03T14:02:26+00:00

Liqua is a project I made during the specialization phase of my education, where I specialized in special effects and graphics programming.

My aim with this project was to establish a framework with various underwater phenomena that together creates a realistic representation of an underwater scene. The direction I chose for the implementations was to create the most convincing effects as cheaply as possible so it remains very feasible for usage in games, while still trying to look as accurate as possible. With this in mind, the implementations are not physically accurate, but are represented closely enough to be convincing.

Everything is made from scratch, the framework is made with modern OpenGL in mind and tries to note make use of any deprecated functionality.

Models credit goes to Robert van Duursen.

Rasterizer

2019-03-03T15:16:32+00:00

The aim of this project was to create a software rasterizer and to learn about what the rasterization process is and how it is handled internally on the GPU.

Next to handling the rasterization process, the project also aimed in learning how to maintain a solid and proper architecture for clear data ownership. The architecture requirement wasn’t only used for rasterization where mesh and material data should have clear ownerships for proper rasterization, but also for the creation of a basic scenegraph where a child and parent node hierarchy was mandatory.

 

Code Snippits

#ifndef _RENDERTARGET_H_
#define _RENDERTARGET_H_

#include "template.h"
#include "surface.h"
#include "Vertex.h"
#include "Span.h"
#include "SceneManager.h"
#include "Core.h"

namespace Tmpl8
{
  class RenderTarget
  {
  public:

    RenderTarget(unsigned int a_Width, unsigned int a_Height);
    void DrawTriangle(const Vertex& a_V1, const Vertex& a_V2, const Vertex& a_V3);
    Surface* GetBuffer() {return m_Buffer;}
    unsigned int GetWidth() {return m_Width;}
    unsigned int GetHeight() {return m_Height;}
    void Clear();
    void SetShading(int a_Shading) {m_CurrentShading = a_Shading;}

    ~RenderTarget();	

  private:

    void ScanEdge(const Vertex& a_V1, const Vertex& a_V2);
    void ResetSpanLine( int line );

    unsigned int m_Width;
    unsigned int m_Height;

    int m_CurrentShading;

    Surface* m_Buffer;
    Color m_Color;
    float* m_ZBuffer;
    Span* m_SpanMax;
    Span* m_SpanMin;

  };
}

#endif

 

#include "RenderTarget.h"

Tmpl8::RenderTarget::RenderTarget( unsigned int a_Width, unsigned int a_Height)
{
  // setting stuff up
  m_CurrentShading = 0;
  m_Width = a_Width;
  m_Height = a_Height;
  m_Buffer = new Surface(m_Width, a_Height);
  m_SpanMax = new Span[m_Height];
  m_SpanMin = new Span[m_Height];
  m_ZBuffer = new float[m_Width * m_Height];

  for( int i = 0; i < m_Height; i++ ) 
    ResetSpanLine( i );

  Clear();
}

void Tmpl8::RenderTarget::DrawTriangle( const Vertex& a_V1, const Vertex& a_V2, const Vertex& a_V3)
{
  ScanEdge(a_V1, a_V2);
  ScanEdge(a_V2, a_V3);
  ScanEdge(a_V3, a_V1);

  int y1 = (int)MIN( a_V1.GetTransformedVertex().y, MIN( a_V2.GetTransformedVertex().y, a_V3.GetTransformedVertex().y ) );
  int y2 = (int)MAX( a_V1.GetTransformedVertex().y, MAX( a_V2.GetTransformedVertex().y, a_V3.GetTransformedVertex().y ) );

  float t_x, t_y, t_z, t_u, t_v, t_nx, t_ny, t_nz;

  // Now this is a story of a little triangle that wanted nothing more than to be a colorfull little thing
  for( int y = y1; y <= y2; y++ )
  {
    if( ( y < m_Buffer->GetHeight() ) && ( y >= 0 ) && ( m_SpanMin[y].GetVertX() < m_SpanMax[y].GetVertX() ) )
    {
      int yw = y * m_Buffer->GetWidth();
      // It had some starting values, but that wasn't enough yet!
      float z = m_SpanMin[y].GetVertZ();
      float u = m_SpanMin[y].GetU();
      float v = m_SpanMin[y].GetV();
      float nx = m_SpanMin[y].GetNormalX();
      float ny = m_SpanMin[y].GetNormalY();
      float nz = m_SpanMin[y].GetNormalZ();
      // It decided to check its entire body, where each color should come
      float dx = 1.f / ( m_SpanMax[y].GetVertX() - m_SpanMin[y].GetVertX() );
      float dz = ( m_SpanMax[y].GetVertZ() - m_SpanMin[y].GetVertZ() ) * dx;
      float du = ( m_SpanMax[y].GetU() - m_SpanMin[y].GetU() ) * dx;
      float dv = ( m_SpanMax[y].GetV() - m_SpanMin[y].GetV() ) * dx;
      float dnx = ( m_SpanMax[y].GetNormalX() - m_SpanMin[y].GetNormalX() ) * dx;
      float dny = ( m_SpanMax[y].GetNormalY() - m_SpanMin[y].GetNormalY() ) * dx;
      float dnz = ( m_SpanMax[y].GetNormalZ() - m_SpanMin[y].GetNormalZ() ) * dx;
      // And when the timing was right..
      for( int x = (int)m_SpanMin[y].GetVertX(); x < (int)m_SpanMax[y].GetVertX(); x++ )
      {
        if( x >= m_Buffer->GetWidth() ) break;
        if( x < 0 )
        {
          const int minx = -x;
          z += dz * minx;
          u += du * minx;
          v += dv * minx;
          nx += dnx * minx;
          ny += dny * minx;
          nz += dnz * minx;
          x = 0;
        }
        if( z > m_ZBuffer[x+yw] )
        {
          m_ZBuffer[x+yw] = z;
          t_z = 1.f / z;
          t_x = x * t_z;
          t_y = y * t_z;
          t_u = u * t_z;
          t_v = v * t_z;
          t_nx = nx * t_z;
          t_ny = ny * t_z;
          t_nz = nz * t_z;
          Pixel color;

          if (m_CurrentShading == 1)
          {
            color = Core::Get()->SetShading(vector3(t_x, t_y, t_z), vector3(t_nx, t_ny, t_nz), vector2(t_u, t_v), m_CurrentShading).ToPixel();
   					Color col;
 						col.FromPixel(color);
          }
          else if (m_CurrentShading == 2)
          {
            color = Core::Get()->SetShading(vector3(t_x, t_y, t_z), vector3(t_nx, t_ny, t_nz), vector2(t_u, t_v), m_CurrentShading).ToPixel();
            Color col;
            col.FromPixel(color);
          }
          else if(m_CurrentShading == 0)
            color = Core::Get()->GetTexture() ? Core::Get()->GetUVColor(t_u, t_v) : Core::Get()->GetColor().ToPixel();

          m_Buffer->Plot( x, m_Buffer->GetHeight() - y, color );
        } 
        z += dz;
        u += du;
        v += dv;
        nx += dnx;
        ny += dny;
        nz += dnz;
      }
    } 
    // it became a colorfull little triangle
    ResetSpanLine( y );
  } 

}

void Tmpl8::RenderTarget::ScanEdge( const Vertex& a_V1, const Vertex& a_V2 )
{
  Tmpl8::Vertex v1 = a_V1;
  Tmpl8::Vertex v2 = a_V2;

  if(v1.GetTransformedVertex().y > v2.GetTransformedVertex().y)
    return ScanEdge(v2, v1);

  const vector3 deltaV = v2.GetTransformedVertex() - v1.GetTransformedVertex();
  const vector3 deltaN = v2.GeTransformedtNormal() - v1.GeTransformedtNormal();
  const vector2 deltaUV = v2.GetTransformedUV() - v1.GetTransformedUV();

  float inv_dy = deltaV.y == 0 ? 0 : 1.f / deltaV.y;
  int y1 = (int)v1.GetTransformedVertex().y, y2 = (int)v2.GetTransformedVertex().y;

  float x = v1.GetTransformedVertex().x;
  float z = v1.GetTransformedVertex().z;
  float nx = v1.GeTransformedtNormal().x;
  float ny = v1.GeTransformedtNormal().y;
  float nz = v1.GeTransformedtNormal().z;
  float u = v1.GetTransformedUV().u;
  float v = v1.GetTransformedUV().v;

  float dx = deltaV.x * inv_dy;
  float dz = deltaV.z * inv_dy;
  float dnx = deltaN.x * inv_dy;
  float dny = deltaN.y * inv_dy;
  float dnz = deltaN.z * inv_dy;
  float du = deltaUV.u * inv_dy;
  float dv = deltaUV.v * inv_dy;

  float inc = 1.0f - ((v1.GetTransformedVertex().y)-((int)(v1.GetTransformedVertex().y)));

  // subpixel/texel correction thingies
  y1++;
  x += dx * inc;
  z += dz * inc;
  nx += dnx * inc;
  ny += dny * inc;
  nz += dnz * inc;
  u += du * inc;
  v += dv * inc;

  for( int y = y1; y <= y2; y++ )
  {
    if( y >= 0 && y < m_Buffer->GetHeight() && z > 0 )
    {
      if( x < m_SpanMin[y].GetVertX() )
      {
        m_SpanMin[y].SetVertX(((int)x) + 1);
        m_SpanMin[y].SetVertY((float)y);
        m_SpanMin[y].SetVertZ(z);
        m_SpanMin[y].SetNormalX(nx);
        m_SpanMin[y].SetNormalY(ny);
        m_SpanMin[y].SetNormalZ(nz);
        m_SpanMin[y].SetU(u);
        m_SpanMin[y].SetV(v);
      }
      if( x > m_SpanMax[y].GetVertX() )
      {
        m_SpanMax[y].SetVertX(((int)x) + 1);
        m_SpanMax[y].SetVertY((float)y);
        m_SpanMax[y].SetVertZ(z);
        m_SpanMax[y].SetNormalX(nx);
        m_SpanMax[y].SetNormalY(ny);
        m_SpanMax[y].SetNormalZ(nz);
        m_SpanMax[y].SetU(u);
        m_SpanMax[y].SetV(v);
      }
    }
    x += dx;
    z += dz;
    nx += dnx;
    ny += dny;
    nz += dnz;
    u += du;
    v += dv;
  }
}

void Tmpl8::RenderTarget::ResetSpanLine( int line )
{
  if( line < 0 || line >= m_Buffer->GetHeight() )
    return;

  m_SpanMax[line].SetVertX(0);
  m_SpanMin[line].SetVertX((float)m_Buffer->GetWidth() - 1);
}

void Tmpl8::RenderTarget::Clear()
{
  m_Buffer->Clear(0x111111);

  if( m_ZBuffer )
  {
    for( int i = 0; i < m_Buffer->GetWidth() * m_Buffer->GetHeight(); i++ )
    {
      m_ZBuffer[i] = 0;
    }
  }
}

Tmpl8::RenderTarget::~RenderTarget()
{
  delete m_Buffer;
  delete m_ZBuffer;
  delete m_SpanMax;
  delete m_SpanMin;
}

 

Special Effects

2019-03-03T15:16:08+00:00

The aim for this project was to create a collection of special effects commonly found in games. The special effects varied to model shaders, post processing effects and small systems, like a basic particle system and a lens flare system. It was mandatory that all the post processing effect could work separately and combined from each other.

The effects and their implementations were made in an already existing framework as an exercise to work within existing frameworks and build needed functionality into it without breaking the existing code base.

 

Code Snippits

struct input
{
  float4 position		: POSITION;
  float2 uv			: TEXCOORD0;

  float4 copyposition : TEXCOORD1;
  float3 normsunpos : TEXCOORD2;
  float4 sunpos : TEXCOORD3;
  float4 moonpos : TEXCOORD5;
  float4 projpos : TEXCOORD4;
};

struct output 
{
  float4 color : COLOR;
};

output pixelfunction( input IN	, uniform sampler2D gradient1: TEXUNIT0
                , uniform sampler2D gradient2: TEXUNIT1
                , uniform sampler2D suntexture: TEXUNIT2
                , uniform sampler2D sungradient: TEXUNIT3
                , uniform sampler2D moontexture: TEXUNIT4
                , uniform sampler2D moonmasktexture: TEXUNIT5
                , uniform sampler2D moonhalotexture: TEXUNIT6
                , uniform sampler2D starfieldtexture: TEXUNIT7
                , uniform sampler2D cloudfieldtexture: TEXUNIT8
                , uniform float moonsunangle
                , uniform float atmosthickness
                , uniform float skyscroll
                , uniform float lightstrength
                , uniform float clouds
                , uniform float stars
                 )
{
  output OUT;

  float aspect = 1080.0f/1920.0f;	
  float2 suninscreen = IN.sunpos.xy / IN.sunpos.w;
  float2 pixinscreen = IN.projpos.xy / IN.projpos.w;
  float2 sunuv = ((suninscreen - pixinscreen) * 4) + float2(0.5,0.45 / aspect);
  sunuv.y *= aspect;

  float2 pixelheight = float2(IN.copyposition.y,0);
  float gradientsclr = dot(IN.copyposition.xyz,IN.normsunpos.xyz);
  float3 suncolor = tex2D(suntexture,sunuv) * (tex2D(sungradient,pixelheight * atmosthickness) * gradientsclr);

  float2 mooninscreen = IN.moonpos.xy / IN.moonpos.w;
  float2 moonuv = ((mooninscreen - pixinscreen) * 4) + float2(0.5,0.5 / aspect);
  moonuv.y *= aspect;

  float3 moonmaskcolor = clamp(tex2D(moonmasktexture,moonuv) - moonsunangle,0,1);
  float3 moonhalocolor = tex2D(moonhalotexture,moonuv) * clamp(1-IN.normsunpos.y,0,1);
  float3 mooncolor = tex2D(moontexture,moonuv) * moonmaskcolor * tex2D(sungradient,pixelheight);

  float2 skyuv = float2(IN.copyposition.y * atmosthickness,0);
  float3 skycolor = clamp(lerp(tex2D( gradient2, skyuv ),tex2D( gradient1, skyuv ),abs(IN.normsunpos.y * atmosthickness)) * clamp(IN.normsunpos.y * atmosthickness,0.025,1),0,1);

 	float redcorrectscalar = clamp(1.0f-dot((IN.copyposition.xyz),IN.normsunpos.xyz),0,1);
 	float3 redcorrectcolor = ((float3( 1, 0.59, 0.16 ) * 0.90f  * redcorrectscalar)) * (1-clamp(IN.normsunpos.y * atmosthickness,0.0,1)) * clamp(IN.normsunpos.y * atmosthickness,0.0,1);

 	float3 starcolor = tex2D(starfieldtexture,IN.uv + float2(skyscroll * 0.1,0) ) * clamp(abs(IN.normsunpos.y),0,1) * redcorrectscalar;
  if(IN.normsunpos.y > 0)starcolor = float3(0,0,0);
  if(stars == 0)starcolor = float3(0,0,0);

  float4 cloudcolor = tex2D(cloudfieldtexture,IN.uv + float2(skyscroll * 0.2,-skyscroll * 0.1));
  cloudcolor.rgb +=  (tex2D(sungradient,pixelheight * atmosthickness))* (1-abs(IN.normsunpos.y));
  cloudcolor.rgb *= clamp(IN.normsunpos.y,0.025,1);
  cloudcolor.a *=  clouds;

  if(IN.sunpos.z < 0)	suncolor = float3(0,0,0);
  if(IN.moonpos.z < 0) mooncolor = float3(0,0,0);

  float3 correctedcolor = (skycolor - clamp(redcorrectcolor,0,1));
  correctedcolor = clamp(correctedcolor,0,1);
  OUT.color.rgb = lerp((correctedcolor) + suncolor + mooncolor + starcolor,cloudcolor.rgb,cloudcolor.a);
  OUT.color.rgb *= lightstrength;

  return OUT;
}

 

struct input
{
  float4 position		: POSITION;		// (projected) position in screen space
  float4 pixelpos		: TEXCOORD5;		// (projected) position in screen space
  float4 posworld		: TEXCOORD6;
  float4 color		: COLOR;
  float4 pos			: TEXCOORD0;	// position in world space (not projected)
  float3 normal		: TEXCOORD1;
  float2 uv			: TEXCOORD2;
  float3 tangent		: TEXCOORD3;
  float3 binormal		: TEXCOORD4;
  float depth			: TEXCOORD7;
};

#define NEARPLANE 0.01
#define FARPLANE 1000.0

float ZfromDepth( float depth, float zNear, float zFar )
{
  // source: http://olivers.posterous.com/linear-depth-in-glsl-for-real
  // OpenGL only!
  return 2*zFar*zNear / (zFar + zNear - (zFar - zNear)*(2*depth -1));
}

struct output 
{
  float4 color : COLOR;
};
float3 expand(float3 v)
{
  return (v - 0.5) * 2;
}

float texwidth = 1.0f/512 * 10;
output pixelfunction( input IN, 
          uniform float4x4 worldCam, 
          uniform float4x4 ModelCamp, 
          uniform float4 lightPos,
          uniform float3 lightColor, 
          uniform float3 eyePosW, 
          uniform float uvoffset,
          uniform sampler2D normalmap: TEXUNIT0,
          uniform sampler2D reflectmap: TEXUNIT1,
          uniform sampler2D refractmap: TEXUNIT2,
          uniform sampler2D depthmap: TEXUNIT3
           )
{
  output OUT;

  float3 P = IN.pos.xyz;

    float3x3 tangentMatrix = float3x3( normalize( IN.tangent ), 
                                       normalize( IN.binormal ),
                                       normalize( IN.normal.xyz ) );

    float2 screenPosition = IN.pixelpos.xy / IN.pixelpos.w; 
    screenPosition = (screenPosition + 1.0)/2; 
    screenPosition.y = screenPosition.y; 

    float3 normalTex = expand(tex2D(normalmap, IN.uv).xyz);
    normalTex += expand(tex2D(normalmap, (IN.uv + float2(texwidth,0)) + uvoffset * 3.1f).xyz);
    normalTex += expand(tex2D(normalmap, (-IN.uv + float2(-texwidth,0)) + uvoffset * 5.2f).xyz);
    normalTex += expand(tex2D(normalmap, (IN.uv + float2(0,texwidth)) + uvoffset * 1.3f).xyz);
    normalTex += expand(tex2D(normalmap, (-IN.uv + float2(0,-texwidth)) + uvoffset * 4.8f).xyz);
    float3 N = normalize( (normalTex) );
    float3 refractTex = tex2D(refractmap, (screenPosition) - (N.xy * 0.0125f / IN.posworld.z)).xyz;
    float3 reflectTex = tex2D(reflectmap, (screenPosition) - (N.xy * 0.025f / IN.posworld.z)).xyz;

    float waterZdepth = IN.depth;
    float refractZdepth = ZfromDepth(tex2D(depthmap, (screenPosition)).xyz,NEARPLANE,FARPLANE);
    float distance = clamp(1/((refractZdepth - waterZdepth)),0,1);

    N = normalize(mul( N, tangentMatrix )); 	
    float3 L = normalize( lightPos - IN.pos.xyz );
    float3 V = normalize( eyePosW - IN.pos.xyz );
    float3 H = normalize ( L + V );

    float fresnel = dot(V,N);

    float specular = 0;
    float diffuse = saturate(dot(N, L));
    float remappedLambert = diffuse * 0.3 + 0.7;
    if ( diffuse > 0 )
    {
        specular = pow(saturate(dot(N, H)), 100);
    }     

    float4 diffuseColor = float4(0.4,1,1,1);
    float4 specularColor = float4(1,1,1,1) * specular;
    float3 deepColor = float3(0.2784,0.3961,1);

    float3 turqoise = float3(64.0f  / 255.0f, 224.0f  / 255.0f, 208.0f  / 255.0f);
    float3 reflectcolor = clamp(reflectTex * diffuseColor,0,1);
    float3 refractcolor = lerp(deepColor,refractTex * turqoise,distance);

  OUT.color.xyz = lerp(reflectcolor,refractcolor,clamp(fresnel,0,1)) + (specularColor * lightColor);
  return OUT;
}

 

struct input
{
  float4 position		: POSITION;
  float2 uv			: TEXCOORD0;
};

struct output 
{
  float4 color : COLOR;
};
#define NEARPLANE 0.01
#define FARPLANE 1000.0

#define NUM_DOF_SAMPLES 64

float ZfromDepth( float depth, float zNear, float zFar )
{
  // source: http://olivers.posterous.com/linear-depth-in-glsl-for-real
  // OpenGL only!
  return 2*zFar*zNear / (zFar + zNear - (zFar - zNear)*(2*depth -1));
}

float CalculateBlurriness(float curpixelz, float minrange, float maxrange)
{
  return (clamp( (curpixelz  - minrange) / (maxrange - minrange), 0, 1 ) * 2) - 1;
}

output pixelfunction( input IN, uniform sampler2D colortexture: TEXUNIT0, 
                uniform sampler2D depthtexture: TEXUNIT1,
                uniform float2 samples[NUM_DOF_SAMPLES],
                uniform float minrange,
                uniform float maxrange,
                uniform float blursize
                )
{
  output OUT;

  float curpixeldepth = tex2D(depthtexture, IN.uv);
  float curpixelz = abs(ZfromDepth(curpixeldepth,NEARPLANE, FARPLANE));

  float blurriness = CalculateBlurriness(curpixelz,minrange,maxrange);

  float totalweight = 1.0f;
  float3 finalColor = tex2D(colortexture, IN.uv).rgb; 

  int numsamples = NUM_DOF_SAMPLES;

  for(int i = 0; i < numsamples; i++)
  {
    float2 sampleuv = IN.uv + samples[i] * abs(blurriness) * blursize;

    float4 samplecolor = tex2D(colortexture, sampleuv);
    float sampledepth = tex2D(depthtexture, sampleuv);

    float samplez = abs(ZfromDepth(sampledepth, NEARPLANE,FARPLANE));
    float sampleblurriness = CalculateBlurriness(samplez,minrange,maxrange);

    float sampleweight;
    sampleweight = (sampledepth < curpixelz) ? abs(sampleblurriness) : 1.0;

    totalweight += sampleweight;	
    finalColor += samplecolor * sampleweight;
  }

  OUT.color.rgb = (finalColor.rgb / totalweight);	
  return OUT;
}

 

Shader programming

2019-03-03T15:15:53+00:00

A small collection of shaders that showcase a variety of the more common techniques related to lighting. The shaders were made as part of an introduction to shader programming both in terms of how to make the shaders and the different techniques as well as how to apply them to a model and how to manage applying parameters and such.

The shaders are made in Nvidia’s CgFX language and the implementation of the framework is done in a self made OpenGL framework with FreeGlut. The framework is set up in a way to efficiently create and apply a shader and apply it to a model and to easily apply and adjust the needed parameters.

Model credit goes to Dmitri Parkin.

 

Code Snippits

#ifndef _EFFECT_H_
#define _EFFECT_H_

#include "Cg/cgGL.h"
#include <string>
#include <vector>

class Effect
{
public:

  virtual ~Effect();

  static Effect* Create(const std::string& a_Name, const std::string& a_File);

  const std::string& GetName() const {return m_Name;}

  bool Load(const std::string& a_File);
  bool Reload();

  CGpass				GetFirstPass();
  CGpass				GetNextPass();
  CGpass				GetCurrentPass() const { return m_CurrentPass; }
  CGtechnique			GetTechnique() const { return m_Technique; }
  CGtechnique			GetTechnique(const char* a_Name);
  CGeffect			GetCGEffect() const { return m_Effect; }
  void				UpdatePassParameters();
  void				SetCurrentTechnique(CGtechnique a_Technique) {m_Technique = a_Technique;}

  CGparameter			GetParameter(const char* param);
  void				SetParameter1f(const char* param, float val);
  void				SetParameter2f(const char* param, const float* val);
  void				SetParameter3f(const char* param, const float* val);
  void				SetParameter4f(const char* param, const float* val);
  void				SetParameter1i(const char* param, int val);
  void				SetParameter2i(const char* param, const int* val);
  void				SetParameter3i(const char* param, const int* val);
  void				SetParameter4i(const char* param, const int* val);
  void				SetParameter1d(const char* param, double val);
  void				SetParameter2d(const char* param, const double* val);
  void				SetParameter3d(const char* param, const double* val);
  void				SetParameter4d(const char* param, const double* val);
  void				SetParameterMatrix(const char* param, const float* val);
  void				SetParameterTexture(const char* param, unsigned int texture);

  void				Bind();
  void				Unbind();

protected:

  Effect(const std::string& a_Name, const std::string& a_File);

  std::string m_Name;
  std::string m_File;
  CGeffect m_Effect;
  CGtechnique m_Technique;
  CGpass m_CurrentPass;

  std::vector<CGtechnique> m_TechniqueList;

  static CGcontext m_Context;
};
#endif

 

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // For OutputDebugString
#endif
#include <sstream>
#include "Effect.h"
#include <string>

CGcontext Effect::m_Context = NULL;

// Error handler for Cg
void CgErrorHandler( CGcontext context, CGerror error, void* appdata )
{
  if ( error != CG_NO_ERROR )
  {
    std::stringstream ss;
    const char* pStr = cgGetErrorString(error);
    std::string strError = ( pStr == NULL ) ? "" : pStr;
    ss << "Cg ERROR: " << strError << std::endl;

    std::string strListing;
    if ( error == CG_COMPILER_ERROR )
    {
      pStr = cgGetLastListing( context );
      strListing = ( pStr == NULL ) ? "" : pStr;

      ss << strListing << std::endl;
    }
#ifdef _WIN32
    OutputDebugStringA( ss.str().c_str() );
#else
    std::cerr << ss;
#endif
  }
}

Effect::Effect( const std::string& a_Name, const std::string& a_File )
{
  m_Name = a_Name;
  m_File = a_File;
  m_CurrentPass = NULL;

  if(!m_Context)
  {
    m_Context = cgCreateContext();
    cgSetErrorHandler(&CgErrorHandler, NULL);
    cgGLRegisterStates(m_Context);
    cgGLSetManageTextureParameters(m_Context, CG_TRUE);
  }

  Load(a_File);
}

Effect::~Effect()
{
  if(m_Effect != NULL)
  {
   	cgDestroyEffect(m_Effect);
   	m_Effect = NULL;
  }

  if(m_Context != NULL)
  {
   	cgDestroyContext(m_Context);
   	m_Context = NULL;
  }

  cgSetErrorHandler(NULL, NULL);
}

Effect* Effect::Create( const std::string& a_Name, const std::string& a_File )
{
  Effect* effect = new Effect(a_Name, a_File);

  return effect;
}

bool Effect::Load( const std::string& a_File )
{
  m_Effect = cgCreateEffectFromFile(m_Context, a_File.c_str(), NULL);

  if(m_Effect == NULL)
  {
    exit(-1);
  }

  CGtechnique technique = cgGetFirstTechnique(m_Effect);
  while(technique != NULL)
  {
    m_Technique = technique;
    m_TechniqueList.push_back(technique);
    technique = cgGetNextTechnique(technique);		
  }

  if(!m_Technique)
  {
    exit(-1);
  }

  return true;
}

bool Effect::Reload()
{
  cgDestroyEffect(m_Effect);
  m_TechniqueList.clear();
  Load(m_File);

  return true;
}

CGpass Effect::GetFirstPass()
{
  m_CurrentPass = cgGetFirstPass(m_Technique);
  return m_CurrentPass;
}

CGpass Effect::GetNextPass()
{
  m_CurrentPass = cgGetNextPass(m_CurrentPass);
  return m_CurrentPass;
}

void Effect::UpdatePassParameters()
{
  cgUpdatePassParameters(m_CurrentPass);
}

CGparameter Effect::GetParameter( const char* param )
{
  CGparameter p = cgGetEffectParameterBySemantic(m_Effect, param);
  if(p) return p;

  p = cgGetNamedEffectParameter(m_Effect, param);
  if(p) return p;

  return 0;
}

void Effect::SetParameter1f( const char* param, float val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter1f(parameter, val);
}

void Effect::SetParameter2f( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter2f(parameter, val[0], val[1]);
}

void Effect::SetParameter3f( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter3f(parameter, val[0], val[1], val[2]);
}

void Effect::SetParameter4f( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter4f(parameter, val[0], val[1], val[2], val[3]);
}

void Effect::SetParameter1i( const char* param, int val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter1i(parameter, val);
}

void Effect::SetParameter2i( const char* param, const int* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter2i(parameter, val[0], val[1]);
}

void Effect::SetParameter3i( const char* param, const int* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter3i(parameter, val[0], val[1], val[2]);
}

void Effect::SetParameter4i( const char* param, const int* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter4i(parameter, val[0], val[1], val[3], val[4]);
}

void Effect::SetParameter1d( const char* param, double val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter1d(parameter, val);
}

void Effect::SetParameter2d( const char* param, const double* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter2d(parameter, val[0], val[1]);
}

void Effect::SetParameter3d( const char* param, const double* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter3d(parameter, val[0], val[1], val[2]);
}

void Effect::SetParameter4d( const char* param, const double* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter4d(parameter, val[0], val[1], val[3], val[4]);
}

void Effect::SetParameterMatrix( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetMatrixParameterfc(parameter, val);
}

void Effect::SetParameterTexture( const char* param, unsigned int texture )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgGLSetTextureParameter(parameter, texture);
}

void Effect::Bind()
{
  cgSetPassState(m_CurrentPass);
}

void Effect::Unbind()
{
  cgResetPassState(m_CurrentPass);
}

CGtechnique Effect::GetTechnique( const char* a_Name )
{
  for(int i = 0; i < m_TechniqueList.size(); ++i)
  {
    const char* name = cgGetTechniqueName(m_TechniqueList[i]);
    if(strcmp(name, a_Name) != 1)
      return m_TechniqueList[i];
  }
}

 

float Attenuation(float4 p, _Light light)
{
  float d = distance(p, light.Position);
  return 1 / (light.ConstantAttenuation + light.LinearAttenuation * d + light.QuadraticAttenuation * d * d);
}

float4 expand(float4 v) 
{
  return (v-0.5)*2;
}

float4 CalcLambert(float3 position, float3 normal, _Light light)
{
  float3 L = normalize(light.Position - position);
  float A = Attenuation(float4(position, 0), light);
  float D = saturate(dot(normal, L));

  return light.Color * D * A;
}

float4 CalcSpecularity(float3 position, float3 normal, float shine, _Light light)
{
  float3 L = normalize(light.Position - position);
  float3 V = normalize(EyePosition - position);
  float3 H = normalize(L + V);
  float A = Attenuation(float4(position, 0), light);
  float S = pow(max(dot(normal, H), 0), shine);

  return light.Color * S * A;
}

float tex2Dshadow( sampler2D shadowMap, float4 texCoord )
{
    float4 projTexColor = tex2Dproj( shadowMap, texCoord );
    float test = texCoord.z / texCoord.w;
    float val = ( test < ( projTexColor.r  ) ) ? 1.0f : 0.0f;

    // Don't shade pixels that are behind the light.
    return ( texCoord.w < 0 ) ? 1.0f : val;
}

float dualConeSpotlight(float3 P, _Light  light)
{
    float3 V = normalize(P - light.Position);
    float cosOuterCone = light.OuterCosAngle;
    float cosInnerCone = light.InnerCosAngle;
    float cosDirection = dot(V, light.Direction);

    return smoothstep(cosOuterCone, cosInnerCone, cosDirection);
}

void SpotAttenLighting(	_Light  light,
                  float3 P,
                  float3 N,
                  float3 eyePosition,
                  float  shininess,

                  out float4 diffuseResult,
                  out float4 specularResult)
{
    // Compute spotlight effect
    float spotEffect = dualConeSpotlight(P, light);

  float4 lambert = CalcLambert(P, N, light);
  float4 phong = CalcSpecularity(P, N, shininess, light);

  diffuseResult = lambert * spotEffect;
  specularResult = phong * spotEffect;
}

 

#include "General.header"
#include "Functions.header"

void mainVP(in Vertex IN, out Fragment OUT)
{
  OUT.pos = mul(ModelViewProjection, float4(IN.pos, 1));
  OUT.wpos = mul(ModelMatrix, float4(IN.pos, 1));

  float3 worldnormal = normalize(mul(ModelMatrixIT, IN.normal));
  float3 worldbinormal = normalize(mul(ModelMatrixIT, IN.binormal));
  float3 worldtangent = normalize(mul(ModelMatrixIT, IN.tangent));

  OUT.normal = float4(worldnormal, 1.0);
  OUT.binormal = float4(worldbinormal, 1.0);
  OUT.tangent = float4(worldtangent, 1.0);

  OUT.uv = IN.uv;
  OUT.diffuse = IN.diffuse;
}

void mainFP(  in Fragment IN, out float4 color : COLOR)
{
  float4 normalTex = tex2D(g_normalmap, IN.uv);
  float4 diffuse = Material.Diffuse;;
  float4 specular = Material.Specular;
  float4 emissive = Material.Emissive;

  if(Material.DiffuseMap != 0)
  {
    diffuse = tex2D(g_DiffuseMap, IN.uv);
  }
  if(Material.SpecularMap != 0)
  {
    specular = tex2D(g_specularmap, IN.uv);
  }
  if(Material.EmissiveMap != 0)
  {
    emissive = tex2D(g_emissivemap, IN.uv);
  }

  float4x4 tbnmatrix;
  tbnmatrix[0] = IN.tangent; 
  tbnmatrix[1] = IN.binormal;
  tbnmatrix[2] = IN.normal;

  float4 normalSample = expand(normalTex);
  normalSample = mul(normalSample, tbnmatrix);

  float4 lambert;
  float4 phong;

  if(Material.NormalMap != 0)
  {
    lambert = CalcLambert(IN.wpos.xyz, normalSample.xyz, Light);
    phong = CalcSpecularity(IN.wpos.xyz, normalSample.xyz, Material.Shininess, Light);
  }
  else
  {
    lambert = CalcLambert(IN.wpos.xyz, IN.normal, Light);
    phong = CalcSpecularity(IN.wpos.xyz, IN.normal, Material.Shininess, Light);
  }

  float4 ambientfinal = GlobalAmbient * Material.Ambient;
  float4 diffusefinal = lambert * diffuse;
  float4 specularfinal = phong * specular;
  float4 emissivefinal = emissive;

  color = ambientfinal + emissivefinal + (diffusefinal + specularfinal);
}

technique t0
{
    pass p0
    {
        VertexProgram = compile arbvp1 mainVP();
        FragmentProgram = compile arbfp1 mainFP();
    }
}