float Script : STANDARDSGLOBAL < string UIWidget = "none"; string ScriptClass = "scene"; string ScriptOrder = "postprocess"; string ScriptOutput = "color"; string Script = "Technique=Main"; > = 0.8; float4 ClearColor < string UIName = "Background"; string UIWidget = "color"; > = {0.2,0.2,0.2,0.0}; float ClearDepth < string UIWidget = "none"; > = 1.0; float passnumber ; // loop counter, hidden /////////////////////////////////////////////////////////// ///////////////////////////// Render-to-Texture Data ////// /////////////////////////////////////////////////////////// // DECLARE_QUAD_TEX(SceneTexture,SceneSampler,"A8R8G8B8") DECLARE_QUAD_DEPTH_BUFFER(DepthBuffer, "D24S8") DECLARE_QUAD_TEX(Map1,Sampler1,"A8R8G8B8") DECLARE_QUAD_TEX(Map2,Sampler2,"A8R8G8B8") ///////////////////////////////////////////////////// //// Tweakables ///////////////////////////////////// ///////////////////////////////////////////////////// float npasses < float UIStep = 1.0; string UIName = "Diff passes"; > = 4.0f; float Blur < string UIWidget = "slider"; float UIMin = 0; float UIMax = 4; float UIStep = 0.01; > = 1.0; ///////////////// struct BlurVertexOutput { QUAD_REAL4 Position : POSITION; QUAD_REAL4 NU : TEXCOORD0; QUAD_REAL2 CC : TEXCOORD1; QUAD_REAL4 PU : TEXCOORD2; QUAD_REAL4 NV : TEXCOORD3; QUAD_REAL4 PV : TEXCOORD4; }; /////////// Vertex shader for "BlurVertexOutput" /////////// BlurVertexOutput BlurVS( QUAD_REAL3 Position : POSITION, QUAD_REAL3 TexCoord : TEXCOORD0 ) { BlurVertexOutput OUT=(BlurVertexOutput)0; QUAD_REAL2 center; OUT.Position = QUAD_REAL4(Position, 1); QUAD_REAL2 texelSize = QUAD_REAL2((1.0/QuadScreenSize.x), (1.0/QuadScreenSize.y)); QUAD_REAL2 blurSize = Blur * texelSize; #ifdef NO_TEXEL_OFFSET center = TexCoord.xy; #else /* NO_TEXEL_OFFSET */ QUAD_REAL2 off = QuadTexOffset * texelSize; QUAD_REAL2(QuadTexOffset/(QuadScreenSize.x),QuadTexOffset/(QuadScreenSize.y)); center = QUAD_REAL2(TexCoord.xy+off); #endif /* NO_TEXEL_OFFSET */ OUT.CC = center; OUT.NU.xy = QUAD_REAL2(center.x-blurSize.x,center.y); OUT.PU.xy = QUAD_REAL2(center.x+blurSize.x,center.y); OUT.NV.xy = QUAD_REAL2(center.x,center.y-blurSize.y); OUT.PV.xy = QUAD_REAL2(center.x,center.y+blurSize.y); OUT.NU.zw = QUAD_REAL2(center.x-blurSize.x,center.y-blurSize.y); OUT.PU.zw = QUAD_REAL2(center.x+blurSize.x,center.y+blurSize.y); OUT.NV.zw = QUAD_REAL2(center.x-blurSize.x,center.y-blurSize.y); OUT.PV.zw = QUAD_REAL2(center.x+blurSize.x,center.y+blurSize.y); return OUT; } /////// Pixel shader to expand values /////// QUAD_REAL4 ExpandPS(BlurVertexOutput IN,uniform sampler2D InputSampler) : COLOR { QUAD_REAL4 scc = tex2D(InputSampler, IN.CC); QUAD_REAL4 result = scc; QUAD_REAL4 snu = tex2D(InputSampler, IN.NU.xy); QUAD_REAL4 spu = tex2D(InputSampler, IN.PU.xy); QUAD_REAL4 snv = tex2D(InputSampler, IN.NV.xy); QUAD_REAL4 spv = tex2D(InputSampler, IN.PV.xy); QUAD_REAL4 sa = tex2D(InputSampler, IN.NU.zw); QUAD_REAL4 sb = tex2D(InputSampler, IN.PU.zw); QUAD_REAL4 sc = tex2D(InputSampler, IN.NV.zw); QUAD_REAL4 sd = tex2D(InputSampler, IN.PV.zw); QUAD_REAL w; w = 0.0; QUAD_REAL3 c; c = QUAD_REAL3(0,0,0); if (scc.a != 0.0) { // don't bother if there's already a value!!!! if (snu.a == 0) { w += 1.0; c += snu.rgb; } if (spu.a == 0) { w += 1.0; c += spu.rgb; } if (snv.a == 0) { w += 1.0; c += snv.rgb; } if (spv.a == 0) { w += 1.0; c += spv.rgb; } if (sa.a == 0) { w += 0.7; c += 0.7*sa.rgb; } if (sb.a == 0) { w += 0.7; c += 0.7*sb.rgb; } if (sc.a == 0) { w += 0.7; c += 0.7*sc.rgb; } if (sd.a == 0) { w += 0.7; c += 0.7*sd.rgb; } if (w > 0.0) { c = c / w; result = QUAD_REAL4(c.rgb,0.0); // result = QUAD_REAL4(1,0,1,0.0); } } return result; } /*******************************************************************/ /************* TECHNIQUES ******************************************/ /*******************************************************************/ technique Main < string Script = "RenderColorTarget0=Map1;" "RenderDepthStencilTarget=DepthBuffer;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Depth;" "ScriptExternal=color;" "LoopByCount=npasses;" //"LoopBegin;" "LoopGetIndex=passnumber;" // Render Object(s) "Pass=p0;" // Blend Results into accum buffer "Pass=p1;" "LoopEnd;" "Pass=show;"; > { pass p0 < string Script = "RenderColorTarget0=Map2;" "RenderDepthStencilTarget=DepthBuffer;" "Draw=Buffer;"; > { cullmode = none; ZEnable = false; ZWriteEnable = false; AlphaBlendEnable = true; SrcBlend = ONE; DestBlend = ZERO; VertexShader = compile vs_3_0 BlurVS(); PixelShader = compile ps_3_0 ExpandPS(Sampler1); } pass p1 < string Script = "RenderColorTarget0=Map1;" "RenderDepthStencilTarget=DepthBuffer;" "Draw=Buffer;"; > { cullmode = none; ZEnable = false; ZWriteEnable = false; AlphaBlendEnable = true; SrcBlend = ONE; DestBlend = ZERO; VertexShader = compile vs_3_0 BlurVS(); PixelShader = compile ps_3_0 ExpandPS(Sampler2); } pass show < string Script = "RenderColorTarget0=;" "RenderDepthStencilTarget=;" "Draw=Buffer;"; > { cullmode = none; ZEnable = false; ZWriteEnable = false; AlphaBlendEnable = true; SrcBlend = ONE; DestBlend = ZERO; VertexShader = compile vs_2_0 ScreenQuadVS(); PixelShader = compile ps_2_a TexQuadPS(Sampler1); } } /***************************** eof ***/ ]]>; \ sampler SampName = sampler_state { \ texture = ; \ AddressU = AddrMode; \ AddressV = AddrMode; \ MipFilter = LINEAR; \ MinFilter = LINEAR; \ MagFilter = LINEAR; \ }; // // Simple 2D File Textures // // example usage: FILE_TEXTURE_2D(GlowMap,GlowSampler,"myfile.dds") // #define FILE_TEXTURE_2D(TextureName,SamplerName,Diskfile) FILE_TEXTURE_2D_MODAL(TextureName,SamplerName,(Diskfile),WRAP) // // Use this variation of DECLARE_QUAD_TEX() if you want a *scaled* render target // // example usage: DECLARE_SIZED_QUAD_TEX(GlowMap,GlowSampler,"A8R8G8B8",1.0) #define DECLARE_SIZED_QUAD_TEX(TexName,SampName,PixFmt,Multiple) texture TexName : RENDERCOLORTARGET < \ float2 ViewPortRatio = {Multiple,Multiple}; \ int MipLevels = 1; \ string Format = PixFmt ; \ string UIWidget = "None"; \ >; \ sampler SampName = sampler_state { \ texture = ; \ AddressU = CLAMP; \ AddressV = CLAMP; \ MipFilter = POINT; \ MinFilter = LINEAR; \ MagFilter = LINEAR; \ }; // // Use this macro to easily declare typical color render targets // // example usage: DECLARE_QUAD_TEX(ObjMap,ObjSampler,"A8R8G8B8") #define DECLARE_QUAD_TEX(TextureName,SamplerName,PixelFormat) DECLARE_SIZED_QUAD_TEX(TextureName,SamplerName,(PixelFormat),1.0) // // Use this macro to easily declare variable-sized depth render targets // // example usage: DECLARE_SIZED_QUAD_DEPTH_BUFFER(DepthMap,"D24S8",0.5) #define DECLARE_SIZED_QUAD_DEPTH_BUFFER(TextureName,PixelFormat,Multiple) texture TextureName : RENDERDEPTHSTENCILTARGET < \ float2 ViewPortRatio = {Multiple,Multiple}; \ string Format = (PixelFormat); \ string UIWidget = "None"; \ >; // // Use this macro to easily declare typical depth render targets // // example usage: DECLARE_QUAD_DEPTH_BUFFER(DepthMap,"D24S8") #define DECLARE_QUAD_DEPTH_BUFFER(TexName,PixFmt) DECLARE_SIZED_QUAD_DEPTH_BUFFER(TexName,PixFmt,1.0) // // declare exact-sized arbitrary texture // // example usage: DECLARE_SIZED_TEX(BlahMap,BlahSampler,"R32F",128,1) #define DECLARE_SIZED_TEX(Tex,Samp,Fmt,Wd,Ht) texture Tex : RENDERCOLORTARGET < \ float2 Dimensions = { Wd, Ht }; \ string Format = Fmt ; \ string UIWidget = "None"; \ int miplevels=1;\ >; \ sampler Samp = sampler_state { \ texture = ; \ AddressU = CLAMP; \ AddressV = CLAMP; \ MipFilter = NONE; \ MinFilter = LINEAR; \ MagFilter = LINEAR; \ }; // // declare exact-sized square texture, as for shadow maps // // example usage: DECLARE_SQUARE_QUAD_TEX(ShadMap,ShadObjSampler,"A16R16G16B16F",512) #define DECLARE_SQUARE_QUAD_TEX(TexName,SampName,PixFmt,Size) DECLARE_SIZED_TEX(TexName,SampName,(PixFmt),Size,Size) // // likewise for shadow depth targets // // example usage: DECLARE_SQUARE_QUAD_DEPTH_BUFFER(ShadDepth,"D24S8",512) #define DECLARE_SQUARE_QUAD_DEPTH_BUFFER(TextureName,PixelFormat,Size) texture TextureName : RENDERDEPTHSTENCILTARGET < \ float2 Dimensions = { Size, Size }; \ string Format = (PixelFormat) ; \ string UIWidget = "None"; \ >; //////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////// Utility Functions //////// //////////////////////////////////////////////////////////////////////////// // // Scale inputs for use with texture-based lookup tables. A value ranging from zero to one needs // a slight scaling and offset to be sure to point at the centers of the first and last pixels // of that lookup texture. Pass the integer size of the table in TableSize // For now we'll assume that all tables are 1D, square, or cube-shaped -- all axes of equal size // // Cost of this operation for pixel shaders: two const-register // entries and a MAD (one cycle) QUAD_REAL scale_lookup(QUAD_REAL Value,const QUAD_REAL TableSize) { QUAD_REAL scale = ((TableSize - 1.0)/TableSize); QUAD_REAL shift = (0.5 / TableSize); return (scale*Value + shift); } QUAD_REAL2 scale_lookup(QUAD_REAL2 Value,const QUAD_REAL TableSize) { QUAD_REAL scale = ((TableSize - 1.0)/TableSize); QUAD_REAL shift = (0.5 / TableSize); return (scale.xx*Value + shift.xx); } QUAD_REAL3 scale_lookup(QUAD_REAL3 Value,const QUAD_REAL TableSize) { QUAD_REAL scale = ((TableSize - 1.0)/TableSize); QUAD_REAL shift = (0.5 / TableSize); return (scale.xxx*Value + shift.xxx); } // pre-multiply and un-pre-mutliply functions. The precision // of thse operatoions is often limited to 8-bit so don't // always count on them! // The macro value of NV_ALPHA_EPSILON, if defined, is used to // avoid IEEE "NaN" values that may occur when erroneously // dividing by a zero alpha (thanks to Pete Warden @ Apple // Computer for the suggestion in GPU GEMS II) // multiply color by alpha to turn an un-premultipied // pixel value into a premultiplied one QUAD_REAL4 premultiply(QUAD_REAL4 C) { return QUAD_REAL4((C.w*C.xyz),C.w); } #define NV_ALPHA_EPSILON 0.0001 // given a premultiplied pixel color, try to undo the premultiplication. // beware of precision errors QUAD_REAL4 unpremultiply(QUAD_REAL4 C) { #ifdef NV_ALPHA_EPSILON QUAD_REAL a = C.w + NV_ALPHA_EPSILON; return QUAD_REAL4((C.xyz / a),C.w); #else /* ! NV_ALPHA_EPSILON */ return QUAD_REAL4((C.xyz / C.w),C.w); #endif /* ! NV_ALPHA_EPSILON */ } ///////////////////////////////////////////////////////////////////////////////////// // Structure Declaration //////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// struct QuadVertexOutput { QUAD_REAL4 Position : POSITION; QUAD_REAL2 UV : TEXCOORD0; }; ///////////////////////////////////////////////////////////////////////////////////// // Hidden tweakables declared by this .fxh file ///////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// #ifndef NO_TEXEL_OFFSET #ifdef TWEAKABLE_TEXEL_OFFSET QUAD_REAL QuadTexOffset = 0.5; #else /* !TWEAKABLE_TEXEL_OFFSET */ QUAD_REAL QuadTexOffset < string UIWidget="None"; > = 0.5; #endif /* !TWEAKABLE_TEXEL_OFFSET */ QUAD_REAL2 QuadScreenSize : VIEWPORTPIXELSIZE < string UIWidget="None"; >; #endif /* NO_TEXEL_OFFSET */ //////////////////////////////////////////////////////////// ////////////////////////////////// vertex shaders ////////// //////////////////////////////////////////////////////////// QuadVertexOutput ScreenQuadVS( QUAD_REAL3 Position : POSITION, QUAD_REAL3 TexCoord : TEXCOORD0 ) { QuadVertexOutput OUT; OUT.Position = QUAD_REAL4(Position, 1); #ifdef NO_TEXEL_OFFSET OUT.UV = TexCoord.xy; #else /* NO_TEXEL_OFFSET */ QUAD_REAL2 off = QUAD_REAL2(QuadTexOffset/(QuadScreenSize.x),QuadTexOffset/(QuadScreenSize.y)); OUT.UV = QUAD_REAL2(TexCoord.xy+off); #endif /* NO_TEXEL_OFFSET */ return OUT; } ////////////////////////////////////////////////////// ////////////////////////////////// pixel shaders ///// ////////////////////////////////////////////////////// // add glow on top of model QUAD_REAL4 TexQuadPS(QuadVertexOutput IN,uniform sampler2D InputSampler) : COLOR { QUAD_REAL4 texCol = tex2D(InputSampler, IN.UV); return texCol; } QUAD_REAL4 TexQuadBiasPS(QuadVertexOutput IN,uniform sampler2D InputSampler,QUAD_REAL TBias) : COLOR { QUAD_REAL4 texCol = tex2Dbias(InputSampler, QUAD_REAL4(IN.UV,0,TBias)); return texCol; } ////////////////////////////////////////////////////////////////// /// Macros to define passes within Techniques //////////////////// ////////////////////////////////////////////////////////////////// // older HLSL syntax #define TEX_TECH(TechName,SamplerName) technique TechName { \ pass TexturePass { \ VertexShader = compile vs_2_0 ScreenQuadVS(); \ AlphaBlendEnable = false; ZEnable = false; \ PixelShader = compile ps_2_a TexQuadPS(SamplerName); } } #define TEX_BLEND_TECH(TechName,SamplerName) technique TechName { \ pass TexturePass { \ VertexShader = compile vs_2_0 ScreenQuadVS(); \ ZEnable = false; AlphaBlendEnable = true; \ SrcBlend = SrcAlpha; DestBlend = InvSrcAlpha; \ PixelShader = compile ps_2_a TexQuadPS(SamplerName); } } // newer HLSL syntax #define TEX_TECH2(TechName,SamplerName,TargName) technique TechName { \ pass TexturePass < \ string ScriptFunction = "RenderColorTarget0=" (TargName) ";" \ "DrawInternal=Buffer;"; \ > { \ VertexShader = compile vs_2_0 ScreenQuadVS(); \ AlphaBlendEnable = false; ZEnable = false; \ PixelShader = compile ps_2_a TexQuadPS(SamplerName); } } #define TEX_BLEND_TECH2(TechName,SamplerName) technique TechName { \ pass TexturePass < \ string ScriptFunction = "RenderColorTarget0=" (TargName) ";" \ "DrawInternal=Buffer;"; \ > { \ VertexShader = compile vs_2_0 ScreenQuadVS(); \ ZEnable = false; AlphaBlendEnable = true; \ SrcBlend = SrcAlpha; DestBlend = InvSrcAlpha; \ PixelShader = compile ps_2_a TexQuadPS(SamplerName); } } #endif /* _QUAD_FXH */ ////////////// eof /// ]]> // Compile-time flags // feature flags // performance flags //#define USE_NORMALIZATION_CUBEMAP //#define USE_HALF // float for pixel shaders #ifdef USE_HALF #define REAL half #define REAL2 half2 #define REAL3 half3 #define REAL4 half4 #else /* !USE_HALF */ #define REAL float #define REAL2 float2 #define REAL3 float3 #define REAL4 float4 #endif /* !USE_HALF */ float Script : STANDARDSGLOBAL < string UIWidget = "none"; string ScriptClass = "object"; string ScriptOrder = "standard"; string ScriptOutput = "color"; string Script = "Technique=Technique?grisaille:untextured;"; > = 0.8; /////////////////////////////////////////////////////////////// /// TWEAKABLES //////////////////////////////////////////////// /////////////////////////////////////////////////////////////// ////////////////////////////////////////////// spot light half3 LightDir : DIRECTION < string UIName = "Light Direction"; string Object = "DirectionalLight"; string Space = "World"; > = {-0.707f, 0.707f, 0.0f}; ////////////////////////////////////////////// ambient light half4 AmbiLightColor : Ambient < string UIName = "Ambient Light Color"; > = {0.07f, 0.07f, 0.07f, 1}; ////////////////////////////////////////////// surface half4 SurfColor : Diffuse < string UIName = "Surface Color"; string UIWidget = "Color"; > = {1.0f, 0.7f, 0.3f, 1}; float Ks < string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 1.5; float UIStep = 0.01; string UIName = "Specular"; > = 1.0; float SpecExpon : SpecularPower < string UIWidget = "slider"; float UIMin = 1.0; float UIMax = 128.0; float UIStep = 1.0; string UIName = "Specular power"; > = 52.0; float Flatness < string UIWidget = "slider"; float UIMin = 0.01; float UIMax = 20.0; float UIStep = 0.01; string UIName = "Flatness"; > = 8.0; //////////////////////////////////////////////////////// /// TEXTURES /////////////////////////////////////////// //////////////////////////////////////////////////////// texture DiffTexture : Diffuse < string ResourceName = "default_color.dds"; string TextureType = "2D"; >; sampler2D DiffSampler = sampler_state { Texture = ; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = WRAP; AddressV = WRAP; }; ////////////////// #ifdef USE_NORMALIZATION_CUBEMAP #include // only for pixel shaders.... /* half3 my_normalize(half3 v) { half3 v2 = texCUBE(NormalizeSampler,v); return (2*(v2-0.5)); } half4 my_normalize(half4 v) { half3 v2 = texCUBE(NormalizeSampler,v.xyz); return half4((2*(v2-0.5)),1); } */ #define NORM my_normalize #else /* !USE_NORMALIZATION_CUBEMAP */ #define NORM normalize #endif /* !USE_NORMALIZATION_CUBEMAP */ /////////////////////////////////////////////////////////// /************* "UN-TWEAKABLES," TRACKED BY CPU APPLICATION **************/ float4x4 WorldITXf : WorldInverseTranspose ; float4x4 WvpXf : WorldViewProjection ; float4x4 WorldXf : World ; float4x4 ViewIXf : ViewInverse ; float4x4 ViewTXf : ViewTranspose ; float4x4 WorldViewITXf : WorldViewInverseTranspose ; float4x4 WorldViewXf : WorldView ; float4x4 ViewXf : View ; float4x4 ViewITXf : ViewInverseTranspose ; #ifdef USE_TIMER float Timer : TIME ; #else /* !USE_TIMER */ float Timer < string UIName = "Time At Frame Start"; string UIWidget = "slider"; float UIMin = 0.0; float UIMax = 10.0; float UIStep = 0.1; > = 0.0f; #endif /* !USE_TIMER */ //////////////////////////////////////////////////////////////////////////// /// SHADER CODE BEGINS ///////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /* data from application vertex buffer */ struct appdata { half3 Position : POSITION; half4 UV : TEXCOORD0; half4 Normal : NORMAL; }; // used for all other passes struct vertexOutput { half4 HPosition : POSITION; half2 UV : TEXCOORD0; float3 WNormal : TEXCOORD1; float3 WView : TEXCOORD2; half4 DiffCol : COLOR0; }; /*********************************************************/ /*********** vertex shader *******************************/ /*********************************************************/ vertexOutput spinVS(appdata IN) { vertexOutput OUT; OUT.UV = IN.UV.xy; // half3 Nn = normalize(mul(IN.Normal, WorldITXf).xyz); // world coords half3 Ne = normalize(mul(IN.Normal, WorldViewITXf).xyz); // view coords Ne = normalize(half3(Ne.xy,Ne.z*Flatness)); half3 Nn = normalize(mul(Ne, ViewTXf).xyz); // world coords half4 Po = half4(IN.Position.xyz,1); // obj coords OUT.WView = normalize(ViewIXf[3].xyz - Po.xyz); // obj coords OUT.WNormal = Nn; // mul(Po,WorldViewProjXf); // screen clipspace coords float d = dot(-LightDir,Nn); OUT.DiffCol = half4(max(0,d).xxx,1.0); half4 Ph = mul(Po, WvpXf); OUT.HPosition = Ph; return OUT; } /*********************************************************/ /*********** pixel shader ********************************/ /*********************************************************/ half4 nPS(vertexOutput IN) : COLOR { float3 Nn = NORM(IN.WNormal); float3 Vn = NORM(IN.WView); float3 Ln = -LightDir; float3 Hn = NORM(Vn + Ln); float hdn = Ks * pow(dot(Hn,Nn),SpecExpon); half4 sc = half4(hdn.xxx,0); half4 tc = tex2D(DiffSampler,IN.UV); half4 dc = tc * SurfColor * (IN.DiffCol + AmbiLightColor); return dc+sc; } half4 ntPS(vertexOutput IN) : COLOR { float3 Nn = NORM(IN.WNormal); float3 Vn = NORM(IN.WView); float3 Ln = -LightDir; float3 Hn = NORM(Vn + Ln); float hdn = Ks * pow(dot(Hn,Nn),SpecExpon); half4 sc = half4(hdn.xxx,0); half4 dc = SurfColor * (IN.DiffCol + AmbiLightColor); return dc+sc; } //////////////////////////////////////////////////////////////////// /// TECHNIQUES ///////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// technique grisaille < string Script = "Pass=p0;"; > { pass p0 < string Script = "Draw=geometry;"; > { VertexShader = compile vs_2_0 spinVS(); ZEnable = true; ZWriteEnable = true; CullMode = None; AlphaBlendEnable = false; PixelShader = compile ps_2_a nPS(); } } technique untextured < string Script = "Pass=p0;"; > { pass p0 < string Script = "Draw=geometry;"; > { VertexShader = compile vs_2_0 spinVS(); ZEnable = true; ZWriteEnable = true; CullMode = None; AlphaBlendEnable = false; PixelShader = compile ps_2_a ntPS(); } } /***************************** eof ***/ ]]>