Opentk/Source/Examples/OpenGL/1.5/DynamicVBO.cs

196 lines
7.8 KiB
C#

using System;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
namespace Examples.Tutorial
{
[Example("Dynamic Vertex Buffer Objects", ExampleCategory.OpenGL, "VBO", Documentation = "DynamicVBO")]
class T09_VBO_Dynamic: GameWindow
{
/// <summary>Creates a 800x600 window with the specified title.</summary>
public T09_VBO_Dynamic( )
: base( 800, 600 )
{
this.VSync = VSyncMode.Off;
}
#region Particles
static int MaxParticleCount = 2000;
int VisibleParticleCount;
VertexC4ubV3f[] VBO = new VertexC4ubV3f[MaxParticleCount];
ParticleAttribut[] ParticleAttributes = new ParticleAttribut[MaxParticleCount];
// this struct is used for drawing
struct VertexC4ubV3f
{
public byte R, G, B, A;
public Vector3 Position;
public static int SizeInBytes = 16;
}
// this struct is used for updates
struct ParticleAttribut
{
public Vector3 Direction;
public uint Age;
// more stuff could be here: Rotation, Radius, whatever
}
uint VBOHandle;
#endregion Particles
/// <summary>Load resources here.</summary>
/// <param name="e">Not used.</param>
protected override void OnLoad( EventArgs e )
{
GL.ClearColor( .1f, 0f, .1f, 0f );
GL.Enable( EnableCap.DepthTest );
// Setup parameters for Points
GL.PointSize( 5f );
GL.Enable( EnableCap.PointSmooth );
GL.Hint( HintTarget.PointSmoothHint, HintMode.Nicest );
// Setup VBO state
GL.EnableClientState( EnableCap.ColorArray );
GL.EnableClientState( EnableCap.VertexArray );
GL.GenBuffers( 1, out VBOHandle );
// Since there's only 1 VBO in the app, might aswell setup here.
GL.BindBuffer( BufferTarget.ArrayBuffer, VBOHandle );
GL.ColorPointer( 4, ColorPointerType.UnsignedByte, VertexC4ubV3f.SizeInBytes, (IntPtr) 0 );
GL.VertexPointer( 3, VertexPointerType.Float, VertexC4ubV3f.SizeInBytes, (IntPtr) (4*sizeof(byte)) );
Random rnd = new Random( );
Vector3 temp = Vector3.Zero;
// generate some random stuff for the particle system
for ( uint i = 0 ; i < MaxParticleCount ; i++ )
{
VBO[i].R = (byte) rnd.Next( 0, 256 );
VBO[i].G = (byte) rnd.Next( 0, 256 );
VBO[i].B = (byte) rnd.Next( 0, 256 );
VBO[i].A = (byte) rnd.Next( 0, 256 ); // isn't actually used
VBO[i].Position = Vector3.Zero; // all particles are born at the origin
// generate direction vector in the range [-0.25f...+0.25f]
// that's slow enough so you can see particles 'disappear' when they are respawned
temp.X = (float) ( ( rnd.NextDouble( ) - 0.5 ) * 0.5f );
temp.Y = (float) ( ( rnd.NextDouble( ) - 0.5 ) * 0.5f );
temp.Z = (float) ( ( rnd.NextDouble( ) - 0.5 ) * 0.5f );
ParticleAttributes[i].Direction = temp; // copy
ParticleAttributes[i].Age = 0;
}
VisibleParticleCount = 0;
}
protected override void OnUnload(EventArgs e)
{
GL.DeleteBuffers( 1, ref VBOHandle );
}
/// <summary>
/// Called when your window is resized. Set your viewport here. It is also
/// a good place to set up your projection matrix (which probably changes
/// along when the aspect ratio of your window).
/// </summary>
/// <param name="e">Contains information on the new Width and Size of the GameWindow.</param>
protected override void OnResize(EventArgs e)
{
GL.Viewport(0, 0, Width, Height);
GL.MatrixMode(MatrixMode.Projection);
Matrix4 p = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Width / (float)Height, 0.1f, 50.0f);
GL.LoadMatrix(ref p);
GL.MatrixMode(MatrixMode.Modelview);
Matrix4 mv = Matrix4.LookAt(Vector3.UnitZ, Vector3.Zero, Vector3.UnitY);
GL.LoadMatrix(ref mv);
}
/// <summary>
/// Called when it is time to setup the next frame. Add you game logic here.
/// </summary>
/// <param name="e">Contains timing information for framerate independent logic.</param>
protected override void OnUpdateFrame( FrameEventArgs e )
{
if ( Keyboard[Key.Escape] )
{
Exit( );
}
// will update particles here. When using a Physics SDK, it's update rate is much higher than
// the framerate and it would be a waste of cycles copying to the VBO more often than drawing it.
if ( VisibleParticleCount < MaxParticleCount )
VisibleParticleCount++;
Vector3 temp;
for ( int i = MaxParticleCount - VisibleParticleCount ; i < MaxParticleCount ; i++ )
{
if (ParticleAttributes[i].Age >= MaxParticleCount)
{
// reset particle
ParticleAttributes[i].Age = 0;
VBO[i].Position = Vector3.Zero;
} else
{
ParticleAttributes[i].Age += (uint)Math.Max(ParticleAttributes[i].Direction.LengthFast * 10, 1);
Vector3.Multiply( ref ParticleAttributes[i].Direction, (float) e.Time, out temp );
Vector3.Add( ref VBO[i].Position, ref temp, out VBO[i].Position );
}
}
}
/// <summary>
/// Called when it is time to render the next frame. Add your rendering code here.
/// </summary>
/// <param name="e">Contains timing information.</param>
protected override void OnRenderFrame( FrameEventArgs e )
{
this.Title = VisibleParticleCount + " Points. FPS: " + string.Format( "{0:F}", 1.0 / e.Time );
GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
GL.PushMatrix( );
GL.Translate( 0f, 0f, -5f );
// Tell OpenGL to discard old VBO when done drawing it and reserve memory _now_ for a new buffer.
// without this, GL would wait until draw operations on old VBO are complete before writing to it
GL.BufferData( BufferTarget.ArrayBuffer, (IntPtr) ( VertexC4ubV3f.SizeInBytes * MaxParticleCount ), IntPtr.Zero, BufferUsageHint.StreamDraw );
// Fill newly allocated buffer
GL.BufferData( BufferTarget.ArrayBuffer, (IntPtr) ( VertexC4ubV3f.SizeInBytes * MaxParticleCount ), VBO, BufferUsageHint.StreamDraw );
// Only draw particles that are alive
GL.DrawArrays( BeginMode.Points, MaxParticleCount - VisibleParticleCount, VisibleParticleCount );
GL.PopMatrix( );
SwapBuffers( );
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// The 'using' idiom guarantees proper resource cleanup.
// We request 30 UpdateFrame events per second, and unlimited
// RenderFrame events (as fast as the computer can handle).
using (T09_VBO_Dynamic example = new T09_VBO_Dynamic())
{
Utilities.SetWindowTitle(example);
example.Run(60.0, 0.0);
}
}
}
}