Page 1 of 1

Shaders manager & co

Posted: Sat May 31, 2008 10:23 am
by radubolovan
Hello again :)
I've made some structures and classes for managing shaders.
I have no where to upload a demo so here is the code in the next posts.

Enjoy :)

Posted: Sat May 31, 2008 10:29 am
by radubolovan
dswshader.h

Code: Select all

/***************************************************************************
 *   Copyright (C) 2007-2008 by Radu Dumitru Bolovan                       *
 *   radubolovan@darkstarlinux.ro                                          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/**
	@author Radu Dumitru Bolovan <radubolovan@darkstarlinux.ro>
 */

#ifndef __DSWHEELS_SHADER_H__
#define __DSWHEELS_SHADER_H__

#include "irrlicht.h"
using namespace irr;

typedef enum _eShaderParamType
{
	SH_PARAM_TYPE_VERTEX = 0,
	SH_PARAM_TYPE_PIXEL
}eShaderParamType;

typedef enum _eTexParamType
{
	SH_TEX_TYPE_DIFFUSE = 0,
	SH_TEX_TYPE_BUMP,
	SH_TEX_TYPE_SPECULAR
}eTexParamType;

typedef struct _tShaderParam
{
	inline _tShaderParam()
	{
		m_type = SH_PARAM_TYPE_VERTEX;
		strcpy( m_name, "" );
		m_nVanCount = 0;
		m_values = NULL;
	}

	_tShaderParam( char const* name, float const* values, int nValCount, eShaderParamType type );
	~_tShaderParam();

	eShaderParamType	m_type;
	char				m_name[256];
	float*				m_values;
	int					m_nVanCount;
}tShaderParam;

class DSWShaderCallBack : public video::IShaderConstantSetCallBack
{
public:
	inline DSWShaderCallBack() : video::IShaderConstantSetCallBack(){}
	~DSWShaderCallBack()
	{
		uint nIndex;
		for( nIndex = 0; nIndex < m_params.size(); nIndex++ )
		{
			tShaderParam* param = m_params[ nIndex ];
			delete param;
		}
	}

	inline void addShaderParam( char const* name, float const* values, int nValCount, const eShaderParamType& type )
	{
		uint nIndex;
		for( nIndex = 0; nIndex < m_params.size(); nIndex++ )
		{
			if( strcmp( m_params[nIndex]->m_name, name ) == 0 )
				return;
		}
		tShaderParam* param = new tShaderParam( name, values, nValCount, type );
		m_params.push_back( param );
	}

	inline void setParamValues( char const* name, float const* values, int nValCount )
	{
		uint nIndex;
		for( nIndex = 0; nIndex < m_params.size(); nIndex++ )
		{
			if( strcmp( m_params[nIndex]->m_name, name ) == 0 )
			{
				if( m_params[nIndex]->m_nVanCount < nValCount )
					return;
				memcpy( m_params[nIndex]->m_values, values, nValCount * sizeof(float) );
				return;
			}
		}
	}

	inline void OnSetConstants( video::IMaterialRendererServices* s, s32 userData )
	{
		uint nIndex;
		for( nIndex = 0; nIndex < m_params.size(); nIndex++ )
		{
			if( m_params[nIndex]->m_type, SH_PARAM_TYPE_VERTEX )
				s->setVertexShaderConstant( m_params[nIndex]->m_name, m_params[nIndex]->m_values, m_params[nIndex]->m_nVanCount );
			else
				s->setPixelShaderConstant( m_params[nIndex]->m_name, m_params[nIndex]->m_values, m_params[nIndex]->m_nVanCount );
		}
	}

protected:
	core::array<tShaderParam*> m_params;
};

class ShaderManager;

class Shader
{
public:
	inline Shader()
	{
		m_shCallback = NULL;
		strcpy( m_name, "" );
		m_nId = -1;
		m_manager = NULL;
	}
	Shader( char const* name, ShaderManager* man );
	~Shader();

	int load( c8* vsFileName, c8* psFileName, E_MATERIAL_TYPE matType );

	inline int id(){ return m_nId; }

	inline void addParam( char const* name, float const* values, int nValCount, const eShaderParamType& type )
	{
		m_shCallback->addShaderParam( name, values, nValCount, type );
	}

	inline void setParamValues( char const* name, float const* values, int nValCount )
	{
		m_shCallback->setParamValues( name, values, nValCount );
	}

	friend class ShaderManager;

protected:
	DSWShaderCallBack*		m_shCallback;
	char					m_name[256];
	int						m_nId;

	ShaderManager*			m_manager;
};

class ShaderManager
{
public:
	ShaderManager();
	~ShaderManager();

	void init( char const* szAppDir );

	s32 loadShader( char const* shaderName, c8* vsFileName, c8* psFileName, E_MATERIAL_TYPE matType );

	s32 getShader( char const* shaderName );

	Shader* shader( char const* shaderName ) const;

	friend class Shader;

protected:
	char					m_szAppDir[DSW_MAX_PATH];

	E_VERTEX_SHADER_TYPE	m_vsType;
	E_PIXEL_SHADER_TYPE		m_psType;

	core::array<Shader*> m_shaders;

   video::IVideoDriver*        m_driver
};

extern DSWheels::ShaderManager* g_pShaderManager;

#endif //__DSWHEELS_SHADER_H__

Posted: Sat May 31, 2008 10:34 am
by radubolovan
dswshader.cpp

Code: Select all

/***************************************************************************
 *   Copyright (C) 2007-2008 by Radu Dumitru Bolovan                       *
 *   radubolovan@darkstarlinux.ro                                          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/**
	@author Radu Dumitru Bolovan <radubolovan@darkstarlinux.ro>
 */

#include "dswshader.h"

_tShaderParam::_tShaderParam( char const* name, float const* values, int nValCount, eShaderParamType type )
{
	m_type = type;
	strcpy( m_name, name );
	m_nVanCount = nValCount;
	m_values = (float*)malloc( nValCount * sizeof(float) );
	memcpy( m_values, values, nValCount * sizeof(float) );
}

_tShaderParam::~_tShaderParam()
{
	if( m_nVanCount > 0 )
	{
		free( m_values );
	}
}

DSWheels::ShaderManager* g_pShaderManager = NULL;

/************************************************************************************/
Shader::Shader( char const* name, ShaderManager* man )
{
	m_shCallback = new DSWShaderCallBack();
	strcpy( m_name, name );
	m_nId = -1;
	m_manager = man;
}

Shader::~Shader()
{
	delete m_shCallback;
}

int Shader::load( c8* vsFileName, c8* psFileName, E_MATERIAL_TYPE matType )
{
	m_nId = m_manager->m_driver->getGPUProgrammingServices()->addHighLevelShaderMaterialFromFiles( vsFileName, "main", m_manager->m_vsType, psFileName, "main", m_manager->m_psType, m_shCallback, matType );
	return m_nId;
}
/************************************************************************************/


/************************************************************************************/
ShaderManager::ShaderManager()
{
	strcpy( m_szAppDir, "" );

   m_driver = NULL;
	m_vsType = EVST_VS_1_1;
	m_psType = EPST_PS_1_1;
}

ShaderManager::~ShaderManager()
{
	uint nIndex;
	for( nIndex = 0; nIndex < m_shaders.size(); nIndex++ )
	{
		delete m_shaders[nIndex];
	}
}

void ShaderManager::init( char const* szAppDir, video::IVideoDriver* driver )
{
	strcpy( m_szAppDir, szAppDir );

	video::E_DRIVER_TYPE dType = driver->getDriverType();
	if( ( dType == video::EDT_DIRECT3D9 ) || ( dType == video::EDT_DIRECT3D8 ) )
	{
		if( driver->queryFeature( video::EVDF_VERTEX_SHADER_3_0 ) )
			m_vsType = video::EVST_VS_3_0;
		else if( driver->queryFeature( video::EVDF_VERTEX_SHADER_2_0 ) )
			m_vsType = video::EVST_VS_2_0;
		else if( driver->queryFeature( video::EVDF_VERTEX_SHADER_1_1 ) )
			m_vsType = video::EVST_VS_1_1;
	
		if( driver->queryFeature( video::EVDF_VERTEX_SHADER_3_0 ) )
			m_psType = video::EPST_PS_3_0;
		else if( driver->queryFeature( video::EVDF_PIXEL_SHADER_2_0 ) )
			m_psType = video::EPST_PS_2_0;
		else if( driver->queryFeature( video::EVDF_PIXEL_SHADER_1_4 ) )
			m_psType = video::EPST_PS_1_4;
		else if( driver->queryFeature( video::EVDF_PIXEL_SHADER_1_3 ) )
			m_psType = video::EPST_PS_1_3;
		else if( driver->queryFeature( video::EVDF_PIXEL_SHADER_1_2 ) )
			m_psType = video::EPST_PS_1_2;
		else if( driver->queryFeature( video::EVDF_VERTEX_SHADER_1_1 ) )
			m_psType = video::EPST_PS_1_1;
	}
	else if( dType == video::EDT_OPENGL )
	{
		m_vsType = EVST_VS_3_0;
		m_psType = EPST_PS_3_0;
	}

   m_driver = driver;
}

s32 ShaderManager::loadShader( char const* shaderName, c8* vsFileName, c8* psFileName, E_MATERIAL_TYPE matType )
{
	uint nIndex;
	for( nIndex = 0; nIndex < m_shaders.size(); nIndex++ )
	{
		if( strcmp( m_shaders[nIndex]->m_name, shaderName ) == 0 )
		{
			return m_shaders[nIndex]->id();
		}
	}
	Shader* sh = new Shader( shaderName, this );
	int nRet = sh->load( vsFileName, psFileName, matType );
	if( nRet > -1 )
		m_shaders.push_back( sh );
	return nRet;
}

s32 ShaderManager::getShader( char const* shaderName )
{
	uint nIndex;
	for( nIndex = 0; nIndex < m_shaders.size(); nIndex++ )
	{
		if( strcmp( m_shaders[nIndex]->m_name, shaderName ) == 0 )
		{
			return m_shaders[nIndex]->id();
		}
	}
	return -1;
}

Shader* ShaderManager::shader( char const* shaderName ) const
{
	uint nIndex;
	for( nIndex = 0; nIndex < m_shaders.size(); nIndex++ )
	{
		Shader* sh = m_shaders[nIndex];
		if( strcmp( sh->m_name, shaderName ) == 0 )
		{
			return sh;
		}
	}
	return NULL;
}
/************************************************************************************/

Posted: Sat May 31, 2008 10:47 am
by radubolovan
Usage
Initialisations

Code: Select all

g_pShaderManager = new ShaderManager();
	g_pShaderManager->init( "data_directory", driver );
destroying

Code: Select all

delete g_pShaderManager;
Using

Code: Select all

int nShader = g_pShaderManager->loadShader( "my_shader", "path_to_vertex_shader_inside_data_directory", "path_to_fragment_shader_inside_data_directory", video::EMT_SOLID );
You can change EMT_SOLID with any material type.
Adding to vertex shader parameter(s)

Code: Select all

float fVValue = 1.0f;
	g_pShaderManager->shader( "my_shader" )->addParam( "a_Vparameter", &fPValue, 1, SH_PARAM_TYPE_VERTEX );
Adding to fragment shader parameter(s)

Code: Select all

float fPValue = 1.0f;
	g_pShaderManager->shader( "my_shader" )->addParam( "a_Pparameter", &fPValue, 1, SH_PARAM_TYPE_PIXEL );
changing parameters values - you cand do this per frame before smgr->drawAll()

Code: Select all

float fNewValue = 5.0f;
	g_pShaderManager->shader( "my_shader" )->setParamValues( "a_Vparameter", &fNewValue, 1 );

Posted: Sun Jun 01, 2008 2:37 am
by BlindSide
Instead of setting the default to PS 1.1, and VS 1.1, what I like to do is query the maximum supported shader model using driver->queryFeature() and use that instead, it's a good idea to compile for the highest supported shader model even on shaders that fall well below the instruction limit for example PS 3.0, because compiling at a higher model produces much more efficient code and less instructions since the compiler can utilize newly added optimization features.

Cheers

Posted: Mon Jun 02, 2008 7:48 pm
by radubolovan
BlindSide wrote:Instead of setting the default to PS 1.1, and VS 1.1, what I like to do is query the maximum supported shader model using driver->queryFeature() and use that instead, it's a good idea to compile for the highest supported shader model even on shaders that fall well below the instruction limit for example PS 3.0, because compiling at a higher model produces much more efficient code and less instructions since the compiler can utilize newly added optimization features.

Cheers
You're absolutly right :) ... see modifications above

Posted: Mon Jun 02, 2008 8:47 pm
by dlangdev
hi radubolovan,

i'm impressed with the posted code above. good job.

i guess the bar is now raised higher, and i'm hoping to see more interesting code posted that build on top of it.

what i'd like to see are presets, shaders that are basically generic and can be applied to a material on-the-fly. then only then i can tweak the parameters and see these changes real-time.

Posted: Tue Jun 03, 2008 7:08 pm
by radubolovan
Hello dlangdev ... I intend to do that (a shader editor), but before ... I want to add some kind of templates ... Something like writting in a file or more many functions as applyLights() for example, then in other file write "include_file.tsh" (sht = shader template) and simply applyLights() ... it's not difficult, but I have to right some code and right now I don't have the time :( ... but soon :D

Posted: Wed Jun 04, 2008 8:52 pm
by dlangdev
hi radubolovan,

once again, i'd like to express my gratitude to the effort you made, not only that, you posted a real working code here as well.

at the moment, i'm using the code you posted above as a way of getting into a simple hypershader design. no lights are going to be added in this design, though. it's mostly about shaders and its relation to a material.

when implemented, i should be able to have a simple shader objects built onto a simple shader editor.

of course, bump mapping and displacement maps will be there as well.

Posted: Thu Jun 05, 2008 12:02 pm
by radubolovan
dlangdev wrote:... no lights are going to be added in this design, though. it's mostly about shaders and its relation to a material.....
You can export any variables to shader ... any lights variables or ... anything...
example:

Code: Select all

scene::ILightSceneNode* l1 = smgr->addLightSceneNode( ... );
video::SLight ld = l1->getLightData ();

float lightVector[4];
lightVector[0] = ld.AmbientColor.getRed();
lightVector[1] = ld.AmbientColor.getGreen();
lightVector[2] = ld.AmbientColor.getBlue();
lightVector[3] = ld.AmbientColor.getAlpha();
then

Code: Select all

g_pShaderManager->shader( "my_shader" )->addParam( "light_color", &lightVector[0], 4, SH_PARAM_TYPE_PIXEL );
Then in the fragment shader:

Code: Select all

uniform float light_color[4];
and use it as well ...
And you cand export enything you want :)
[EDIT]
but ... here is an example of shader which is using light with no export (but texture):
vertex shader:

Code: Select all

varying vec4 a_color;
void main()
{
	vec3 normal = normalize( gl_NormalMatrix * gl_Normal );
	vec3 position = vec3( gl_ModelViewMatrix * gl_Vertex );
	vec3 lightVector = normalize( gl_LightSource[0].position.xyz - position );
	vec4 diffuse = gl_FrontLightProduct[0].diffuse * max( 0.0, dot( normal,lightVector ) );
	a_color = gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + diffuse;
	
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
	
	gl_TexCoord[0] = gl_MultiTexCoord0;
}
fragment shader:

Code: Select all

varying vec4 a_color;
uniform sampler2D myTexture;
void main()
{
   vec4 col = texture2D( myTexture, vec2( gl_TexCoord[0] ) );
   gl_FragColor = col * a_color;
}
I think I was copying this shaders from irrlicht forums ... anyway it's working ;)