shaders/Common/Shader.cpp

shaders/Common/Shader.cpp
/***************************************************************************************
Autodesk(R) Open Reality(R) Samples
(C) 2009 Autodesk, Inc. and/or its licensors
All rights reserved.
AUTODESK SOFTWARE LICENSE AGREEMENT
Autodesk, Inc. licenses this Software to you only upon the condition that
you accept all of the terms contained in the Software License Agreement ("Agreement")
that is embedded in or that is delivered with this Software. By selecting
the "I ACCEPT" button at the end of the Agreement or by copying, installing,
uploading, accessing or using all or any portion of the Software you agree
to enter into the Agreement. A contract is then formed between Autodesk and
either you personally, if you acquire the Software for yourself, or the company
or other legal entity for which you are acquiring the software.
AUTODESK, INC., MAKES NO WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE REGARDING THESE MATERIALS, AND MAKES SUCH MATERIALS AVAILABLE SOLELY ON AN
"AS-IS" BASIS.
IN NO EVENT SHALL AUTODESK, INC., BE LIABLE TO ANYONE FOR SPECIAL, COLLATERAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES IN CONNECTION WITH OR ARISING OUT OF PURCHASE
OR USE OF THESE MATERIALS. THE SOLE AND EXCLUSIVE LIABILITY TO AUTODESK, INC.,
REGARDLESS OF THE FORM OF ACTION, SHALL NOT EXCEED THE PURCHASE PRICE OF THE
MATERIALS DESCRIBED HEREIN.
Autodesk, Inc., reserves the right to revise and improve its products as it sees fit.
Autodesk and Open Reality are registered trademarks or trademarks of Autodesk, Inc.,
in the U.S.A. and/or other countries. All other brand names, product names, or
trademarks belong to their respective holders.
GOVERNMENT USE
Use, duplication, or disclosure by the U.S. Government is subject to restrictions as
set forth in FAR 12.212 (Commercial Computer Software-Restricted Rights) and
DFAR 227.7202 (Rights in Technical Data and Computer Software), as applicable.
Manufacturer is Autodesk, Inc., 10 Duke Street, Montreal, Quebec, Canada, H3C 2L7.
***************************************************************************************/
#ifdef LINUX
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#endif
#include <GL/glew.h>
#include <Cg/cgGL.h>
#include "Shader.h"
#include <math.h>
namespace Graphics
{
#ifndef APIENTRYP
#define APIENTRYP APIENTRY *
#endif
#ifndef GL_TEXTURE0_ARB
#define GL_TEXTURE0_ARB 0x84c0
#endif
#ifndef GL_ARRAY_BUFFER_ARB
#define GL_ARRAY_BUFFER_ARB 0x8892
#endif
namespace
{
void SetTexture(CGparameter pParamTextureValidVS, CGparameter pParamTextureValidPS, CGparameter pTexture, CGparameter pMatrix, unsigned int pTextureObject, double* pTextureMatrix)
{
if (pTextureObject > 0)
{
cgSetParameter1i( pParamTextureValidVS, 1 );
cgSetParameter1i( pParamTextureValidPS, 1 );
cgGLSetTextureParameter( pTexture, pTextureObject );
cgGLEnableTextureParameter( pTexture );
cgSetMatrixParameterdc( pMatrix, pTextureMatrix );
}
else
{
cgSetParameter1i( pParamTextureValidVS, 0 );
cgSetParameter1i( pParamTextureValidPS, 0 );
}
}
void SetMaterial(CGparameter pParamAmbient, CGparameter pParamDiffuse, CGparameter pParamSpecular, CGparameter pParamEmissive,
float* pAmbient, float* pDiffuse, float* pSpecular, float* pEmission, float pShininess)
{
cgSetParameter4fv( pParamAmbient, pAmbient );
cgSetParameter4fv( pParamDiffuse, pDiffuse );
float pBuffer[4];
pBuffer[0] = pSpecular[0];
pBuffer[1] = pSpecular[1];
pBuffer[2] = pSpecular[2];
pBuffer[3] = pShininess;
cgSetParameter4fv( pParamSpecular, pBuffer );
cgSetParameter4fv( pParamEmissive, pEmission );
}
double sDefaultAttenuationNone[3] = { 1.0, 0.0, 0.0 };
double sDefaultAttenuationLinear[3] = { 0.0, 0.01, 0.0 };
double sDefaultAttenuationQuadratic[3] = { 0.0, 0.0, 0.0001 };
}
Shader::Shader()
:mContext( NULL )
,mVertexShader( NULL )
,mPixelShader( NULL )
{
static bool init = true;
if ( init )
{
GLenum err = glewInit();
if (GLEW_OK != err)
{
// Problem: glewInit failed, something is seriously wrong.
FBTrace("GLEW error: %s\n", glewGetErrorString(err));
}
else
{
FBTrace("GLEW version: %s\n", glewGetString(GLEW_VERSION));
}
init = false;
}
}
Shader::~Shader()
{
// Destroy shaders
if ( mVertexShader )
{
cgDestroyProgram( mVertexShader );
}
if ( mPixelShader )
{
cgDestroyProgram( mPixelShader );
}
// Destroy the cg context (and subsequently everything in it)
if ( mContext )
{
cgDestroyContext( mContext );
}
}
bool Shader::Initialize(const char* pVertexShaderPath, const char* pPixelShaderPath, const char* pVertexShaderOptions, const char* pPixelShaderOptions)
{
// Create the cg context (need to create anything in cg)
mContext = cgCreateContext();
if( !mContext )
{
ShowError("Could not create CG context");
return false;
}
// Setup for best shader model
CGprofile lVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
CGprofile lPixelProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
if( !cgGLIsProfileSupported(lVertexProfile) )
{
ShowError("Vertex programming extensions not supported");
return false;
}
if( !cgGLIsProfileSupported(lPixelProfile) )
{
ShowError("Pixel programming extensions not supported");
return false;
}
cgGLSetOptimalOptions(lVertexProfile);
cgGLSetOptimalOptions(lPixelProfile);
mVertexProfile = lVertexProfile;
mPixelProfile = lPixelProfile;
// Create shaders
if( !CreateVertexShader( pVertexShaderPath, pVertexShaderOptions ) )
{
return false;
}
if( !CreatePixelShader( pPixelShaderPath, pPixelShaderOptions ) )
{
return false;
}
return true;
}
void Shader::BeginShading(FBRenderOptions* pRenderOptions, FBArrayTemplate<FBLight*>* pAffectingLightList)
{
BindShaderPrograms();
UploadParameters(pRenderOptions, pAffectingLightList);
}
void Shader::EndShading()
{
UnsetTextures();
UnbindShaderPrograms();
}
void Shader::BindShaderPrograms()
{
cgGLBindProgram(mVertexShader); //Bind program. (You can bind only one vertex and one fragment program at a time)
cgGLBindProgram(mPixelShader);
// Set the pixel/vertex shader profiles
cgGLEnableProfile(mVertexProfile);
cgGLEnableProfile(mPixelProfile);
}
void Shader::UnbindShaderPrograms()
{
cgGLDisableProfile(mVertexProfile);
cgGLDisableProfile(mPixelProfile);
cgGLUnbindProgram(mVertexProfile);
cgGLUnbindProgram(mPixelProfile);
}
void Shader::UploadParameters( FBRenderOptions* pRenderOptions, FBArrayTemplate<FBLight*>* pAffectingLightList )
{
if (pRenderOptions->IsIDBufferRendering())
return;
// Get the camera's location ...
FBRenderer* lRender = FBSystem::TheOne().Renderer;
FBMatrix lCameraMVMatrix;
lRender->CurrentCamera->GetCameraMatrix( lCameraMVMatrix, kFBModelView );
FBRVector lViewRotation;
FBMatrixToRotation(lViewRotation, lCameraMVMatrix);
FBMatrix lViewRotationMatrix;
FBRotationToMatrix(lViewRotationMatrix, lViewRotation);
// Get lighting info
// Start with global ambient.
FBColorAndAlpha lAmbienColor = (FBColor)FBGlobalLight::TheOne().AmbientColor;
cgSetParameter4dv( mParamLightAmbient, (double*)lAmbienColor );
FBArrayTemplate<FBLight*> lLightList;
int lLightCount = kMaxLight;
// If not AffectingLights specified, we choose the light from the scene.
// Ignore any more than 8 lights .. all MB supports
if (pAffectingLightList && pAffectingLightList->GetCount() )
{
if (pAffectingLightList->GetCount() < lLightCount)
lLightCount = pAffectingLightList->GetCount();
for (int i = 0; i < lLightCount; ++i)
{
FBLight* lFBLight = pAffectingLightList->GetAt(i);
if (lFBLight)
lLightList.Add( lFBLight );
}
}
else
{
FBPropertyListLight* lSceneLightsList = &FBSystem::TheOne().Scene->Lights;
if (lSceneLightsList->GetCount() < lLightCount)
lLightCount = lSceneLightsList->GetCount();
for (int i = 0; i < lLightCount; ++i)
{
FBLight* lFBLight = FBCast<FBLight>(lSceneLightsList->GetAt(i)->GetHIObject());
if (lFBLight)
lLightList.Add( lFBLight );
}
}
lLightCount = lLightList.GetCount();
if ( lLightCount == 0 )
{
// If there is no light in the scene, we must put the two default lights
// in order to have almost the same behavior as MB. Those two lights are
// not present as soon as there is one light in the scene.
const FBVector4d kPosition(0,0,1,0);
const FBVector4d kDiffuse(0.8,0.8,0.8,0);
const FBVector4d kAttenuation(1,0,0,1);
const FBVector4d kDirection0(0.2,-0.2,-0.6,0);
const FBVector4d kDirection1(-0.6,-0.4,0.75,0);
FBVector4d lViewPosition;
FBVectorMatrixMult(lViewPosition, lCameraMVMatrix, kPosition);
cgSetParameter4dv( mParamLightPositions[0], lViewPosition.mValue );
cgSetParameter4dv( mParamLightColours[0], kDiffuse.mValue );
cgSetParameter4dv( mParamLightAttenuations[0], kAttenuation.mValue );
FBVector4d lDirection0;
FBVectorMatrixMult(lDirection0, lViewRotationMatrix, kDirection0);
cgSetParameter4dv( mParamLightDirections[0], lDirection0.mValue );
cgSetParameter4dv( mParamLightPositions[1], lViewPosition.mValue );
cgSetParameter4dv( mParamLightColours[1], kDiffuse.mValue );
cgSetParameter4dv( mParamLightAttenuations[1], kAttenuation.mValue );
FBVector4d lDirection1;
FBVectorMatrixMult(lDirection1, lViewRotationMatrix, kDirection1);
cgSetParameter4dv( mParamLightDirections[1], lDirection1.mValue );
lLightCount = 2;
}
else
{
// For each scene's light.
for( int i = 0; i < lLightCount; ++i )
{
FBLight* light = lLightList[i];
// Get the light's position, direction, colour, and cone angle (stored in direction's 4th component)
// Also setup attenuation for the lighting equation ...
// Note that for point/spot lights, we add a 1.0 to the 4th colour
// component so we can know to generate the direction from position
FBVector4d position;
light->GetVector(*(FBVector3d*)(&position)); // Get Global Translation.
// Convert the ModelView (World) space
FBVector4d lViewPosition;
FBVectorMatrixMult(lViewPosition, lCameraMVMatrix, position);
// Set whether or not we take the spot factor into consideration in the last position spot ...
lViewPosition[3] = (light->LightType == kFBLightTypeSpot)?1.0:0.0;
cgSetParameter4dv( mParamLightPositions[i], lViewPosition.mValue );
// Setup color and attenuation
FBColorAndAlpha diffuseColor = (FBColor)light->DiffuseColor;
double attenuation[4];
if( light->LightType != kFBLightTypeInfinite )
{
switch(light->AttenuationType)
{
attenuation[0] = sDefaultAttenuationLinear[0];
attenuation[1] = sDefaultAttenuationLinear[1];
attenuation[2] = sDefaultAttenuationLinear[2];
break;
//assert(light->AttenuationType != kFBAttenuationCubic);// "Don't support Cubic attenuation yet, use Quadratic instead.");
attenuation[0] = sDefaultAttenuationQuadratic[0];
attenuation[1] = sDefaultAttenuationQuadratic[1];
attenuation[2] = sDefaultAttenuationQuadratic[2];
break;
default:
attenuation[0] = sDefaultAttenuationNone[0];
attenuation[1] = sDefaultAttenuationNone[1];
attenuation[2] = sDefaultAttenuationNone[2];
break;
}
attenuation[3] = light->Intensity / 100.0;
diffuseColor.mValue[3] = 1.0;
}
else
{
attenuation[0] = 1.0; attenuation[1] = 0.0; attenuation[2] = 0.0;
attenuation[3] = light->Intensity/100.0;
diffuseColor.mValue[3] = 0.0;
}
cgSetParameter4dv( mParamLightColours[i], (double*)diffuseColor );
cgSetParameter4dv( mParamLightAttenuations[i], attenuation );
// Get the rotation matrix and multiply light vector with it
FBMatrix rotationMatrix;
FBVector4d direction;
light->GetMatrix( rotationMatrix, kModelRotation, true );
FBVector4d normal(0, -1, 0, 1);
FBVectorMatrixMult( direction, rotationMatrix, normal );
// Convert the ModelView (World) space
FBVector4d lViewDirection;
FBVectorMatrixMult(lViewDirection, lViewRotationMatrix, direction);
if( light->LightType == kFBLightTypeSpot )
lViewDirection.mValue[3] = cos( (3.141592654*light->OuterAngle/180.0f)/2.0f);
else
lViewDirection.mValue[3] = 0.0;
cgSetParameter4dv( mParamLightDirections[i], lViewDirection.mValue);
}
}
cgSetParameter1i( mParamLightCount, lLightCount );
}
void Shader::UnsetTextures()
{
// Deactivate all.
cgGLDisableTextureParameter( mParamColorTexture );
cgGLDisableTextureParameter( mParamNormalMap );
}
CGprogram Shader::CreateShader(CGprofile pProfile, const char* pPath, const char** pArgs)
{
// Create shader based file.
CGprogram lProgram = cgCreateProgramFromFile( mContext, CG_SOURCE, pPath,
pProfile, NULL, pArgs );
// If shader was created ok, load it so it's ready to bind at render time
if( lProgram )
{
cgGLLoadProgram( lProgram );
}
else
{
// Throw an error if something went wrong
ShowError( cgGetLastListing(mContext) );
}
return lProgram;
}
bool Shader::CreateVertexShader( const char* pPath, const char* pOptions )
{
// Parse compilation option.
const int MAX_ARGS = 10;
const char* lArgs[MAX_ARGS];
int lArgCount = 0;
char* lOptions = NULL;
if (pOptions)
{
lOptions = strdup(pOptions);
char* lOption = strtok(lOptions," ");
while (lOption)
{
if (lArgCount>=MAX_ARGS)
{
// Too many arguments
free(lOptions);
return false;
}
lArgs[lArgCount++] = lOption;
lOption = strtok(NULL," ");
}
}
lArgs[lArgCount] = NULL;
// Create it.
mVertexShader = CreateShader(mVertexProfile, pPath, lArgs);
if (lOptions)
{
free(lOptions);
}
if ( !mVertexShader )
{
return false;
}
// Now that the shader are created, store the parameters so
// we can upload data to them at render time
//mParamModelViewArrayBuffer = cgCreateBuffer(mContext, kMaxDrawInstancedSize * 4 * 4 * sizeof(double), NULL, CG_BUFFER_USAGE_DYNAMIC_DRAW);
//cgSetProgramBuffer(mVertexShader, cgGetParameterBufferIndex(cgGetNamedParameter(mVertexShader, "ModelViewArrayBuffer")), mParamModelViewArrayBuffer);
//mParamModelViewArrayBufferOffset = cgGetParameterBufferOffset(cgGetNamedParameter(mVertexShader, "ModelViewArrayBuffer"));
mParamColorTextureValidVS = cgGetNamedParameter( mVertexShader, "ColorTextureValid" );
mParamColorTextureMatrix = cgGetNamedParameter( mVertexShader, "ColorTextureMatrix" );
mParamNormalMapValidVS = cgGetNamedParameter( mVertexShader, "NormalMapValid" );
mParamNormalMapMatrix = cgGetNamedParameter( mVertexShader, "NormalMapMatrix" );
assert(mParamColorTextureValidVS);
assert(mParamColorTextureMatrix);
assert(mParamNormalMapValidVS);
assert(mParamNormalMapMatrix);
return true;
}
bool Shader::CreatePixelShader( const char* pPath, const char* pOptions )
{
// Parse compilation option.
const int MAX_ARGS = 10;
const char* lArgs[MAX_ARGS];
int lArgCount = 0;
char* lOptions = NULL;
if (pOptions)
{
lOptions = strdup(pOptions);
char* lOption = strtok(lOptions," ");
while (lOption)
{
if (lArgCount>=(MAX_ARGS-1))
{
// Too many arguments
free(lOptions);
return false;
}
lArgs[lArgCount++] = lOption;
lOption = strtok(NULL," ");
}
}
char lMaxLightArg[64];
sprintf(lMaxLightArg, "-DMAX_LIGHT_COUNT=%d", kMaxLight);
lArgs[lArgCount++] = lMaxLightArg;
lArgs[lArgCount] = NULL;
// Create it.
mPixelShader = CreateShader(mPixelProfile, pPath, lArgs);
if (lOptions)
{
free(lOptions);
}
if ( !mPixelShader )
{
return false;
}
// Now that the shader are created, store the parameters so
// we can upload data to them at render time
mParamMatEmissive = cgGetNamedParameter( mPixelShader, "MaterialEmission" );
mParamMatAmbient = cgGetNamedParameter( mPixelShader, "MaterialAmbient" );
mParamMatDiffuse = cgGetNamedParameter( mPixelShader, "MaterialDiffuse" );
mParamMatSpecular = cgGetNamedParameter( mPixelShader, "MaterialSpecular" );
mParamLightAmbient = cgGetNamedParameter( mPixelShader, "GlobalAmbientLight" );
mParamColorTexture = cgGetNamedParameter( mPixelShader, "ColorTexture" );
mParamColorTextureValidPS = cgGetNamedParameter( mPixelShader, "ColorTextureValid" );
mParamNormalMap = cgGetNamedParameter( mPixelShader, "NormalMap" );
mParamNormalMapValidPS = cgGetNamedParameter( mPixelShader, "NormalMapValid" );
mParamIDBufferRender = cgGetNamedParameter( mPixelShader, "IDBufferRender" );
mParamLightCount = cgGetNamedParameter( mPixelShader, "LightCount" );
assert(mParamMatEmissive);
assert(mParamMatAmbient);
assert(mParamMatDiffuse);
assert(mParamMatSpecular);
assert(mParamLightAmbient);
assert(mParamColorTexture);
assert(mParamColorTextureValidPS);
assert(mParamNormalMap);
assert(mParamNormalMapValidPS);
assert(mParamIDBufferRender);
assert(mParamLightCount);
char variableName[64];
for( int i = 0; i < kMaxLight; ++i )
{
sprintf_s( variableName, "LightPositions[%d]", i );
mParamLightPositions[i] = cgGetNamedParameter( mPixelShader, variableName );
sprintf_s( variableName, "LightDirections[%d]", i );
mParamLightDirections[i] = cgGetNamedParameter( mPixelShader, variableName );
sprintf_s( variableName, "LightColors[%d]", i );
mParamLightColours[i] = cgGetNamedParameter( mPixelShader, variableName );
sprintf_s( variableName, "LightAttenuations[%d]", i );
mParamLightAttenuations[i] = cgGetNamedParameter( mPixelShader, variableName );
assert(mParamLightDirections[i]);
assert(mParamLightColours[i]);
assert(mParamLightAttenuations[i]);
}
return true;
}
void Shader::SwitchMaterial(FBRenderOptions* pRenderOptions, FBShaderModelInfo* pShaderModelInfo, FBMaterial* pMaterial, double pShaderTransparencyFactor)
{
if (pMaterial)
{
//Setup Basic Material Property.
FBColor lAmbientColor = pMaterial->Ambient;
FBColor lDiffuseColor = pMaterial->Diffuse;
FBColor lSpecularColor = pMaterial->Specular;
FBColor lEmissiveColor = pMaterial->Emissive;
float lShininess = pMaterial->Shininess;
float lAmbient[4], lDiffuse[4], lSpecular[4], lEmissive[4];
#define ConvertColor( floatColor, doubleColor ) { floatColor[0] = doubleColor[0], floatColor[1] = doubleColor[1], floatColor[2] = doubleColor[2], floatColor[3] = 1.0f; }
ConvertColor(lAmbient, lAmbientColor);
ConvertColor(lDiffuse, lDiffuseColor);
ConvertColor(lSpecular, lSpecularColor);
ConvertColor(lEmissive, lEmissiveColor);
#undef ConvertColor
lDiffuse[3] = mAlpha = (1.0 - pMaterial->TransparencyFactor) * pShaderTransparencyFactor;
if (pRenderOptions->IsIDBufferRendering())
{
SetTexture(mParamNormalMapValidVS, mParamNormalMapValidPS, mParamNormalMap, mParamNormalMapMatrix, 0, NULL);
}
else
{
SetMaterial(mParamMatAmbient, mParamMatDiffuse, mParamMatSpecular, mParamMatEmissive,
lAmbient, lDiffuse, lSpecular, lEmissive, lShininess);
FBTexture* lNormalMapTexture = pMaterial->GetTexture(kFBMaterialTextureBump);
if (lNormalMapTexture == NULL)
lNormalMapTexture = pMaterial->GetTexture(kFBMaterialTextureNormalMap);
if (lNormalMapTexture)
{
lNormalMapTexture->OGLInit();
SetTexture(mParamNormalMapValidVS, mParamNormalMapValidPS, mParamNormalMap, mParamNormalMapMatrix, lNormalMapTexture->GetTextureObject(), lNormalMapTexture->GetMatrix());
}
else
{
SetTexture(mParamNormalMapValidVS, mParamNormalMapValidPS, mParamNormalMap, mParamNormalMapMatrix, 0, NULL);
}
}
cgSetParameter1i(mParamIDBufferRender, pRenderOptions->IsIDBufferRendering() ? 1 : 0);
//Setup Texture if desirable channel exists. for this case, we look up diffuse and normalmap channel
FBTexture* lDiffuseTexture = NULL;
if (pShaderModelInfo->GetOriginalTextureFlag())
lDiffuseTexture = pMaterial->GetTexture(kFBMaterialTextureDiffuse);
if (lDiffuseTexture)
{
lDiffuseTexture->OGLInit();
SetTexture(mParamColorTextureValidVS, mParamColorTextureValidPS, mParamColorTexture, mParamColorTextureMatrix, lDiffuseTexture->GetTextureObject(), lDiffuseTexture->GetMatrix());
}
else
SetTexture(mParamColorTextureValidVS, mParamColorTextureValidPS, mParamColorTexture, mParamColorTextureMatrix, 0, NULL);
}
}
void Shader::UploadModelViewMatrixArrayForDrawInstanced(const double* pModelViewMatrixArray, int pCount)
{
//cgSetBufferSubData(mParamModelViewArrayBuffer, mParamModelViewArrayBufferOffset, 4 * 4 * sizeof (double) * pCount, pModelViewMatrixArray);
}
void Shader::ShaderPassModelDraw ( FBRenderOptions* pRenderOptions, FBRenderingPass pPass, FBShaderModelInfo* pInfo)
{
if (pRenderOptions->IsIDBufferRendering())
{
FBColor lColorId = pInfo->GetFBModel()->UniqueColorId;
float lDiffuse[4] = {lColorId[0], lColorId[1], lColorId[2], mAlpha}; // * pRenderOptions->GetIDBufferPickingAlphaThreshold()};
cgSetParameter4fv(mParamMatDiffuse, lDiffuse);
}
}
void Shader::ShowError(const char* pText)
{
const char* lErrStr = ( pText ) ? pText : cgGetErrorString(cgGetError());
FBMessageBox("Cg Error", (char*)lErrStr, "OK");
}
} // Graphics namespace