2009-10-24 10:07:43 +00:00
|
|
|
|
#region --- License ---
|
|
|
|
|
/* Licensed under the MIT/X11 license.
|
|
|
|
|
* Copyright (c) 2006-2008 the OpenTK Team.
|
|
|
|
|
* This notice may not be removed from any source distribution.
|
|
|
|
|
* See license.txt for licensing details.
|
|
|
|
|
*/
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
|
|
using OpenTK;
|
|
|
|
|
using OpenTK.Graphics;
|
|
|
|
|
using OpenTK.Graphics.OpenGL;
|
|
|
|
|
|
|
|
|
|
using Examples.TextureLoaders;
|
|
|
|
|
|
|
|
|
|
namespace Examples.Tutorial
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Demonstrates Swizzled DXT5 Parallax Mapping
|
|
|
|
|
/// The idea is described in more detail right here: http://www.opentk.com/node/394
|
|
|
|
|
/// </summary>
|
2010-10-02 18:52:34 +00:00
|
|
|
|
[Example("Swizzled Parallax Mapping", ExampleCategory.OpenGL, "2.x", Documentation = "SwizzledParallax")]
|
2009-10-24 10:07:43 +00:00
|
|
|
|
public class T12_GLSL_Parallax: GameWindow
|
|
|
|
|
{
|
|
|
|
|
public T12_GLSL_Parallax( )
|
|
|
|
|
: base( 800, 600 )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region internal Fields
|
|
|
|
|
|
|
|
|
|
// Shader
|
|
|
|
|
int VertexShaderObject, FragmentShaderObject, ProgramObject;
|
|
|
|
|
const string VertexShaderFilename = "Data/Shaders/Parallax_VS.glsl";
|
|
|
|
|
const string FragmentShaderFilename = "Data/Shaders/Parallax_FS.glsl";
|
|
|
|
|
|
|
|
|
|
const int AttribTangent = 5; // slot where to pass tangents to VS, not sure which are reserved besides 0
|
|
|
|
|
Vector3 Tangent = new Vector3( 1f, 0f, 0f );
|
|
|
|
|
Vector3 Normal = new Vector3( 0f, 0f, 1f );
|
|
|
|
|
|
|
|
|
|
// Material parameter
|
|
|
|
|
//Vector3 MaterialScaleAndBiasAndShininess = new Vector3( 0.07f, 0.0f, 38.0f ); // for Metal tex
|
|
|
|
|
Vector3 MaterialScaleAndBiasAndShininess = new Vector3( 0.04f, 0.0f, 92.0f ); // for Rock tex
|
|
|
|
|
|
|
|
|
|
// Textures
|
|
|
|
|
const TextureUnit TMU0_Unit = TextureUnit.Texture0;
|
|
|
|
|
const int TMU0_UnitInteger = 0;
|
2009-10-28 09:05:13 +00:00
|
|
|
|
const string TMU0_Filename = "Data/Textures/swizzled-rock-diffuse-height.dds";
|
2009-10-24 10:07:43 +00:00
|
|
|
|
uint TMU0_Handle;
|
|
|
|
|
TextureTarget TMU0_Target;
|
|
|
|
|
|
|
|
|
|
const TextureUnit TMU1_Unit = TextureUnit.Texture1;
|
|
|
|
|
const int TMU1_UnitInteger = 1;
|
2009-10-28 09:05:13 +00:00
|
|
|
|
const string TMU1_Filename = "Data/Textures/swizzled-rock-normal-gloss.dds";
|
2009-10-24 10:07:43 +00:00
|
|
|
|
uint TMU1_Handle;
|
|
|
|
|
TextureTarget TMU1_Target;
|
|
|
|
|
|
|
|
|
|
// Camera
|
|
|
|
|
Vector3 EyePos = new Vector3( 0.0f, 0.0f, 3.0f );
|
|
|
|
|
|
|
|
|
|
// Light
|
|
|
|
|
Vector3 LightPosition = new Vector3( 0.0f, 1.0f, 1.0f );
|
|
|
|
|
Vector3 LightDiffuse = new Vector3( 0.5f, 0.5f, 0.5f );
|
|
|
|
|
Vector3 LightSpecular = new Vector3( 1f, 1f, 1f );
|
|
|
|
|
|
|
|
|
|
#endregion internal Fields
|
|
|
|
|
|
|
|
|
|
/// <summary>Setup OpenGL and load resources here.</summary>
|
|
|
|
|
/// <param name="e">Not used.</param>
|
|
|
|
|
protected override void OnLoad(EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
this.VSync = VSyncMode.Off;
|
|
|
|
|
|
|
|
|
|
// Check for necessary capabilities:
|
2009-10-28 09:05:13 +00:00
|
|
|
|
string extensions = GL.GetString(StringName.Extensions);
|
|
|
|
|
if ( !GL.GetString(StringName.Extensions).Contains("GL_ARB_shading_language"))
|
2009-10-24 10:07:43 +00:00
|
|
|
|
{
|
2009-10-28 09:05:13 +00:00
|
|
|
|
throw new NotSupportedException(String.Format("This example requires OpenGL 2.0. Found {0}. Aborting.",
|
|
|
|
|
GL.GetString(StringName.Version).Substring(0, 3)));
|
2009-10-24 10:07:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-28 09:05:13 +00:00
|
|
|
|
if (!extensions.Contains("GL_ARB_texture_compression") ||
|
|
|
|
|
!extensions.Contains("GL_EXT_texture_compression_s3tc"))
|
2009-10-24 10:07:43 +00:00
|
|
|
|
{
|
2009-10-28 09:05:13 +00:00
|
|
|
|
throw new NotSupportedException("This example requires support for texture compression. Aborting.");
|
2009-10-24 10:07:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int[] temp = new int[1];
|
|
|
|
|
GL.GetInteger( GetPName.MaxTextureImageUnits, out temp[0] );
|
|
|
|
|
Trace.WriteLine( temp[0] + " TMU's for Fragment Shaders found. (2 required)" );
|
|
|
|
|
|
|
|
|
|
GL.GetInteger( GetPName.MaxVaryingFloats, out temp[0] );
|
|
|
|
|
Trace.WriteLine( temp[0] + " varying floats between VS and FS allowed. (6 required)" );
|
|
|
|
|
|
|
|
|
|
GL.GetInteger( GetPName.MaxVertexUniformComponents, out temp[0] );
|
|
|
|
|
Trace.WriteLine( temp[0] + " uniform components allowed in Vertex Shader. (6 required)" );
|
|
|
|
|
|
|
|
|
|
GL.GetInteger( GetPName.MaxFragmentUniformComponents, out temp[0] );
|
|
|
|
|
Trace.WriteLine( temp[0] + " uniform components allowed in Fragment Shader. (11 required)" );
|
|
|
|
|
Trace.WriteLine("" );
|
|
|
|
|
|
|
|
|
|
#region GL State
|
|
|
|
|
|
|
|
|
|
GL.ClearColor( 0.2f, 0f, 0.4f, 0f );
|
|
|
|
|
|
|
|
|
|
GL.PointSize( 10f );
|
|
|
|
|
|
|
|
|
|
GL.Disable( EnableCap.Dither );
|
|
|
|
|
|
|
|
|
|
GL.FrontFace( FrontFaceDirection.Ccw );
|
|
|
|
|
GL.PolygonMode( MaterialFace.Front, PolygonMode.Fill );
|
|
|
|
|
GL.PolygonMode( MaterialFace.Back, PolygonMode.Line );
|
|
|
|
|
|
|
|
|
|
#endregion GL State
|
|
|
|
|
|
|
|
|
|
#region Shaders
|
|
|
|
|
|
|
|
|
|
string LogInfo;
|
|
|
|
|
|
|
|
|
|
// Load&Compile Vertex Shader
|
|
|
|
|
|
|
|
|
|
using ( StreamReader sr = new StreamReader( VertexShaderFilename ) )
|
|
|
|
|
{
|
|
|
|
|
VertexShaderObject = GL.CreateShader( ShaderType.VertexShader );
|
|
|
|
|
GL.ShaderSource( VertexShaderObject, sr.ReadToEnd( ) );
|
|
|
|
|
GL.CompileShader( VertexShaderObject );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GL.GetShaderInfoLog( VertexShaderObject, out LogInfo );
|
|
|
|
|
if ( LogInfo.Length > 0 && !LogInfo.Contains( "hardware" ) )
|
|
|
|
|
Trace.WriteLine( "Vertex Shader failed!\nLog:\n" + LogInfo );
|
|
|
|
|
else
|
|
|
|
|
Trace.WriteLine( "Vertex Shader compiled without complaint." );
|
|
|
|
|
|
|
|
|
|
// Load&Compile Fragment Shader
|
|
|
|
|
|
|
|
|
|
using ( StreamReader sr = new StreamReader( FragmentShaderFilename ) )
|
|
|
|
|
{
|
|
|
|
|
FragmentShaderObject = GL.CreateShader( ShaderType.FragmentShader );
|
|
|
|
|
GL.ShaderSource( FragmentShaderObject, sr.ReadToEnd( ) );
|
|
|
|
|
GL.CompileShader( FragmentShaderObject );
|
|
|
|
|
}
|
|
|
|
|
GL.GetShaderInfoLog( FragmentShaderObject, out LogInfo );
|
|
|
|
|
|
|
|
|
|
if ( LogInfo.Length > 0 && !LogInfo.Contains( "hardware" ) )
|
|
|
|
|
Trace.WriteLine( "Fragment Shader failed!\nLog:\n" + LogInfo );
|
|
|
|
|
else
|
|
|
|
|
Trace.WriteLine( "Fragment Shader compiled without complaint." );
|
|
|
|
|
|
|
|
|
|
// Link the Shaders to a usable Program
|
|
|
|
|
ProgramObject = GL.CreateProgram( );
|
|
|
|
|
GL.AttachShader( ProgramObject, VertexShaderObject );
|
|
|
|
|
GL.AttachShader( ProgramObject, FragmentShaderObject );
|
|
|
|
|
|
|
|
|
|
// must bind the attribute before linking
|
|
|
|
|
GL.BindAttribLocation( ProgramObject, AttribTangent, "AttributeTangent" );
|
|
|
|
|
|
|
|
|
|
// link it all together
|
|
|
|
|
GL.LinkProgram( ProgramObject );
|
|
|
|
|
|
|
|
|
|
// flag ShaderObjects for delete when not used anymore
|
|
|
|
|
GL.DeleteShader( VertexShaderObject );
|
|
|
|
|
GL.DeleteShader( FragmentShaderObject );
|
|
|
|
|
|
|
|
|
|
GL.GetProgram( ProgramObject, ProgramParameter.LinkStatus, out temp[0] );
|
|
|
|
|
Trace.WriteLine( "Linking Program (" + ProgramObject + ") " + ( ( temp[0] == 1 ) ? "succeeded." : "FAILED!" ) );
|
|
|
|
|
if ( temp[0] != 1 )
|
|
|
|
|
{
|
|
|
|
|
GL.GetProgramInfoLog( ProgramObject, out LogInfo );
|
|
|
|
|
Trace.WriteLine( "Program Log:\n" + LogInfo );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GL.GetProgram( ProgramObject, ProgramParameter.ActiveAttributes, out temp[0] );
|
|
|
|
|
Trace.WriteLine( "Program registered " + temp[0] + " Attributes. (Should be 4: Pos, UV, Normal, Tangent)" );
|
|
|
|
|
|
|
|
|
|
Trace.WriteLine( "Tangent attribute bind location: " + GL.GetAttribLocation( ProgramObject, "AttributeTangent" ) );
|
|
|
|
|
|
|
|
|
|
Trace.WriteLine( "End of Shader build. GL Error: " + GL.GetError( ) );
|
|
|
|
|
|
|
|
|
|
#endregion Shaders
|
|
|
|
|
|
|
|
|
|
#region Textures
|
|
|
|
|
|
|
|
|
|
TextureLoaderParameters.MagnificationFilter = TextureMagFilter.Linear;
|
|
|
|
|
TextureLoaderParameters.MinificationFilter = TextureMinFilter.LinearMipmapLinear;
|
|
|
|
|
TextureLoaderParameters.WrapModeS = TextureWrapMode.ClampToBorder;
|
|
|
|
|
TextureLoaderParameters.WrapModeT = TextureWrapMode.ClampToBorder;
|
|
|
|
|
TextureLoaderParameters.EnvMode = TextureEnvMode.Modulate;
|
|
|
|
|
|
|
|
|
|
ImageDDS.LoadFromDisk( TMU0_Filename, out TMU0_Handle, out TMU0_Target );
|
|
|
|
|
Trace.WriteLine( "Loaded " + TMU0_Filename + " with handle " + TMU0_Handle + " as " + TMU0_Target );
|
|
|
|
|
|
|
|
|
|
ImageDDS.LoadFromDisk( TMU1_Filename, out TMU1_Handle, out TMU1_Target );
|
|
|
|
|
Trace.WriteLine( "Loaded " + TMU1_Filename + " with handle " + TMU1_Handle + " as " + TMU1_Target );
|
|
|
|
|
|
|
|
|
|
#endregion Textures
|
|
|
|
|
|
|
|
|
|
Trace.WriteLine( "End of Texture Loading. GL Error: " + GL.GetError( ) );
|
|
|
|
|
Trace.WriteLine( "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnUnload(EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
GL.DeleteProgram( ProgramObject );
|
|
|
|
|
GL.DeleteTextures( 1, ref TMU0_Handle );
|
|
|
|
|
GL.DeleteTextures( 1, ref TMU1_Handle );
|
|
|
|
|
|
|
|
|
|
base.OnUnload( e );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Respond to resize events here.</summary>
|
|
|
|
|
/// <param name="e">Contains information on the new GameWindow size.</param>
|
|
|
|
|
/// <remarks>There is no need to call the base implementation.</remarks>
|
|
|
|
|
protected override void OnResize( EventArgs e )
|
|
|
|
|
{
|
|
|
|
|
GL.Viewport( 0, 0, Width, Height );
|
|
|
|
|
|
|
|
|
|
GL.MatrixMode( MatrixMode.Projection );
|
2009-11-04 20:48:31 +00:00
|
|
|
|
Matrix4 p = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Width / (float)Height, 0.1f, 100.0f);
|
2009-10-24 10:07:43 +00:00
|
|
|
|
GL.LoadMatrix(ref p);
|
|
|
|
|
|
|
|
|
|
GL.MatrixMode( MatrixMode.Modelview );
|
|
|
|
|
GL.LoadIdentity( );
|
|
|
|
|
|
|
|
|
|
base.OnResize( e );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Add your game logic here.</summary>
|
|
|
|
|
/// <param name="e">Contains timing information.</param>
|
|
|
|
|
/// <remarks>There is no need to call the base implementation.</remarks>
|
|
|
|
|
protected override void OnUpdateFrame( FrameEventArgs e )
|
|
|
|
|
{
|
|
|
|
|
base.OnUpdateFrame( e );
|
|
|
|
|
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.Escape] )
|
|
|
|
|
this.Exit( );
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.Space] )
|
|
|
|
|
Trace.WriteLine( "GL: " + GL.GetError( ) );
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.Q] )
|
|
|
|
|
{
|
|
|
|
|
MaterialScaleAndBiasAndShininess.X += 0.01f;
|
|
|
|
|
Trace.WriteLine( "Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y );
|
|
|
|
|
}
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.A] )
|
|
|
|
|
{
|
|
|
|
|
MaterialScaleAndBiasAndShininess.X -= 0.01f;
|
|
|
|
|
Trace.WriteLine( "Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y );
|
|
|
|
|
}
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.W] )
|
|
|
|
|
{
|
|
|
|
|
MaterialScaleAndBiasAndShininess.Y += 0.01f;
|
|
|
|
|
Trace.WriteLine( "Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y );
|
|
|
|
|
}
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.S] )
|
|
|
|
|
{
|
|
|
|
|
MaterialScaleAndBiasAndShininess.Y -= 0.01f;
|
|
|
|
|
Trace.WriteLine( "Scale: " + MaterialScaleAndBiasAndShininess.X + " Bias: " + MaterialScaleAndBiasAndShininess.Y );
|
|
|
|
|
}
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.E] )
|
|
|
|
|
{
|
|
|
|
|
MaterialScaleAndBiasAndShininess.Z += 1f;
|
|
|
|
|
Trace.WriteLine( "Shininess: " + MaterialScaleAndBiasAndShininess.Z );
|
|
|
|
|
}
|
|
|
|
|
if ( Keyboard[OpenTK.Input.Key.D] )
|
|
|
|
|
{
|
|
|
|
|
MaterialScaleAndBiasAndShininess.Z -= 1f;
|
|
|
|
|
Trace.WriteLine( "Shininess: " + MaterialScaleAndBiasAndShininess.Z );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LightPosition.X = ( -( this.Width / 2 ) + Mouse.X ) / 100f;
|
|
|
|
|
LightPosition.Y = ( ( this.Height / 2 ) - Mouse.Y ) / 100f;
|
|
|
|
|
|
|
|
|
|
EyePos.Y = Mouse.Wheel * 0.5f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Add your game rendering code here.</summary>
|
|
|
|
|
/// <param name="e">Contains timing information.</param>
|
|
|
|
|
/// <remarks>There is no need to call the base implementation.</remarks>
|
|
|
|
|
protected override void OnRenderFrame( FrameEventArgs e )
|
|
|
|
|
{
|
2009-11-04 20:48:31 +00:00
|
|
|
|
this.Title = "FPS: " + (1 / e.Time).ToString("0.");
|
2009-10-24 10:07:43 +00:00
|
|
|
|
|
|
|
|
|
GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
|
|
|
|
|
|
|
|
|
|
GL.UseProgram( ProgramObject );
|
|
|
|
|
|
|
|
|
|
#region Textures
|
|
|
|
|
|
|
|
|
|
GL.ActiveTexture( TMU0_Unit );
|
|
|
|
|
GL.BindTexture( TMU0_Target, TMU0_Handle );
|
|
|
|
|
GL.ActiveTexture( TMU1_Unit );
|
|
|
|
|
GL.BindTexture( TMU1_Target, TMU1_Handle );
|
|
|
|
|
|
|
|
|
|
#endregion Textures
|
|
|
|
|
|
|
|
|
|
#region Uniforms
|
|
|
|
|
|
|
|
|
|
// first Material's uniforms
|
|
|
|
|
GL.Uniform1( GL.GetUniformLocation( ProgramObject, "Material_DiffuseAndHeight" ), TMU0_UnitInteger );
|
|
|
|
|
GL.Uniform1( GL.GetUniformLocation( ProgramObject, "Material_NormalAndGloss" ), TMU1_UnitInteger );
|
|
|
|
|
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Material_ScaleBiasShininess" ), MaterialScaleAndBiasAndShininess.X, MaterialScaleAndBiasAndShininess.Y, MaterialScaleAndBiasAndShininess.Z );
|
|
|
|
|
|
|
|
|
|
// the rest are vectors
|
|
|
|
|
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Camera_Position" ), EyePos.X, EyePos.Y, EyePos.Z );
|
|
|
|
|
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Light_Position" ), LightPosition.X, LightPosition.Y, LightPosition.Z );
|
|
|
|
|
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Light_DiffuseColor" ), LightDiffuse.X, LightDiffuse.Y, LightDiffuse.Z );
|
|
|
|
|
GL.Uniform3( GL.GetUniformLocation( ProgramObject, "Light_SpecularColor" ), LightSpecular.X, LightSpecular.Y, LightSpecular.Z );
|
|
|
|
|
|
|
|
|
|
#endregion Uniforms
|
|
|
|
|
|
|
|
|
|
GL.PushMatrix( );
|
|
|
|
|
Matrix4 t = Matrix4.LookAt( EyePos, Vector3.Zero, Vector3.UnitY );
|
|
|
|
|
GL.MultMatrix(ref t);
|
|
|
|
|
|
|
|
|
|
#region Draw Quad
|
|
|
|
|
|
|
|
|
|
GL.Color3( 1f, 1f, 1f );
|
|
|
|
|
|
|
|
|
|
GL.Begin( BeginMode.Quads );
|
|
|
|
|
{
|
|
|
|
|
GL.Normal3( Normal );
|
|
|
|
|
GL.VertexAttrib3( AttribTangent, ref Tangent );
|
|
|
|
|
GL.MultiTexCoord2( TextureUnit.Texture0, 0f, 1f );
|
|
|
|
|
GL.Vertex3( -1.0f, 1.0f, 0.0f );
|
|
|
|
|
|
|
|
|
|
GL.Normal3( Normal );
|
|
|
|
|
GL.VertexAttrib3( AttribTangent, ref Tangent );
|
|
|
|
|
GL.MultiTexCoord2( TextureUnit.Texture0, 0f, 0f );
|
|
|
|
|
GL.Vertex3( -1.0f, -1.0f, 0.0f );
|
|
|
|
|
|
|
|
|
|
GL.Normal3( Normal );
|
|
|
|
|
GL.VertexAttrib3( AttribTangent, ref Tangent );
|
|
|
|
|
GL.MultiTexCoord2( TextureUnit.Texture0, 1f, 0f );
|
|
|
|
|
GL.Vertex3( 1.0f, -1.0f, 0.0f );
|
|
|
|
|
|
|
|
|
|
GL.Normal3( Normal );
|
|
|
|
|
GL.VertexAttrib3( AttribTangent, ref Tangent );
|
|
|
|
|
GL.MultiTexCoord2( TextureUnit.Texture0, 1f, 1f );
|
|
|
|
|
GL.Vertex3( 1.0f, 1.0f, 0.0f );
|
|
|
|
|
}
|
|
|
|
|
GL.End( );
|
|
|
|
|
|
|
|
|
|
#endregion Draw Quad
|
|
|
|
|
|
|
|
|
|
GL.UseProgram( 0 );
|
|
|
|
|
|
|
|
|
|
// visualize the light position 'somehow'
|
|
|
|
|
GL.Begin( BeginMode.Points );
|
|
|
|
|
{
|
|
|
|
|
GL.Color3( LightSpecular );
|
|
|
|
|
GL.Vertex3( LightPosition );
|
|
|
|
|
}
|
|
|
|
|
GL.End( );
|
|
|
|
|
|
|
|
|
|
GL.PopMatrix( );
|
|
|
|
|
|
|
|
|
|
this.SwapBuffers( );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Entry point</summary>
|
|
|
|
|
[STAThread]
|
|
|
|
|
public static void Main( )
|
|
|
|
|
{
|
|
|
|
|
using ( T12_GLSL_Parallax example = new T12_GLSL_Parallax( ) )
|
|
|
|
|
{
|
|
|
|
|
Utilities.SetWindowTitle( example );
|
|
|
|
|
example.Run( 30.0, 0.0 );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|