338 строки
8.2 KiB
C#
338 строки
8.2 KiB
C#
using System;
|
|
|
|
using OpenTK;
|
|
using OpenTK.Graphics.ES20;
|
|
using GL1 = OpenTK.Graphics.ES11.GL;
|
|
using All1 = OpenTK.Graphics.ES11.All;
|
|
using OpenTK.Platform.iPhoneOS;
|
|
|
|
using Foundation;
|
|
using CoreAnimation;
|
|
using ObjCRuntime;
|
|
using OpenGLES;
|
|
using UIKit;
|
|
|
|
namespace MyOpenGLApp
|
|
{
|
|
[Register ("EAGLView")]
|
|
public class EAGLView : iPhoneOSGameView
|
|
{
|
|
[Export ("initWithCoder:")]
|
|
public EAGLView (NSCoder coder) : base (coder)
|
|
{
|
|
LayerRetainsBacking = true;
|
|
LayerColorFormat = EAGLColorFormat.RGBA8;
|
|
}
|
|
|
|
[Export ("layerClass")]
|
|
public static new Class GetLayerClass ()
|
|
{
|
|
return iPhoneOSGameView.GetLayerClass ();
|
|
}
|
|
|
|
protected override void ConfigureLayer (CAEAGLLayer eaglLayer)
|
|
{
|
|
eaglLayer.Opaque = true;
|
|
}
|
|
|
|
protected override void CreateFrameBuffer ()
|
|
{
|
|
try {
|
|
ContextRenderingApi = EAGLRenderingAPI.OpenGLES2;
|
|
base.CreateFrameBuffer ();
|
|
} catch (Exception) {
|
|
ContextRenderingApi = EAGLRenderingAPI.OpenGLES1;
|
|
base.CreateFrameBuffer ();
|
|
}
|
|
|
|
if (ContextRenderingApi == EAGLRenderingAPI.OpenGLES2)
|
|
LoadShaders ();
|
|
}
|
|
|
|
protected override void DestroyFrameBuffer ()
|
|
{
|
|
base.DestroyFrameBuffer ();
|
|
DestroyShaders ();
|
|
}
|
|
|
|
#region DisplayLink support
|
|
|
|
int frameInterval;
|
|
CADisplayLink displayLink;
|
|
|
|
public bool IsAnimating { get; private set; }
|
|
|
|
// How many display frames must pass between each time the display link fires.
|
|
public int FrameInterval {
|
|
get {
|
|
return frameInterval;
|
|
}
|
|
set {
|
|
if (value <= 0)
|
|
throw new ArgumentException ();
|
|
frameInterval = value;
|
|
if (IsAnimating) {
|
|
StopAnimating ();
|
|
StartAnimating ();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void StartAnimating ()
|
|
{
|
|
if (IsAnimating)
|
|
return;
|
|
|
|
CreateFrameBuffer ();
|
|
displayLink = UIScreen.MainScreen.CreateDisplayLink (this, new Selector ("drawFrame"));
|
|
displayLink.FrameInterval = frameInterval;
|
|
displayLink.AddToRunLoop (NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
|
|
|
|
IsAnimating = true;
|
|
}
|
|
|
|
public void StopAnimating ()
|
|
{
|
|
if (!IsAnimating)
|
|
return;
|
|
|
|
displayLink.Invalidate ();
|
|
displayLink = null;
|
|
DestroyFrameBuffer ();
|
|
IsAnimating = false;
|
|
}
|
|
|
|
[Export ("drawFrame")]
|
|
void DrawFrame ()
|
|
{
|
|
OnRenderFrame (new FrameEventArgs ());
|
|
}
|
|
|
|
#endregion
|
|
|
|
static readonly float[] squareVertices = {
|
|
-0.5f, -0.33f,
|
|
0.5f, -0.33f,
|
|
-0.5f, 0.33f,
|
|
0.5f, 0.33f,
|
|
};
|
|
|
|
static readonly byte[] squareColors = {
|
|
255, 255, 0, 255,
|
|
0, 255, 255, 255,
|
|
0, 0, 0, 0,
|
|
255, 0, 255, 255,
|
|
};
|
|
|
|
static float transY = 0.0f;
|
|
|
|
const int UNIFORM_TRANSLATE = 0;
|
|
const int UNIFORM_COUNT = 1;
|
|
|
|
readonly int[] uniforms = new int [UNIFORM_COUNT];
|
|
|
|
const int ATTRIB_VERTEX = 0;
|
|
const int ATTRIB_COLOR = 1;
|
|
const int ATTRIB_COUNT = 2;
|
|
|
|
int program;
|
|
|
|
protected override void OnRenderFrame (FrameEventArgs e)
|
|
{
|
|
base.OnRenderFrame (e);
|
|
|
|
MakeCurrent ();
|
|
|
|
// Replace the implementation of this method to do your own custom drawing.
|
|
GL.ClearColor (0.5f, 0.5f, 0.5f, 1.0f);
|
|
GL.Clear (ClearBufferMask.ColorBufferBit);
|
|
|
|
if (ContextRenderingApi == EAGLRenderingAPI.OpenGLES2) {
|
|
// Use shader program.
|
|
GL.UseProgram (program);
|
|
|
|
// Update uniform value.
|
|
GL.Uniform1 (uniforms [UNIFORM_TRANSLATE], transY);
|
|
transY += 0.075f;
|
|
|
|
// Update attribute values.
|
|
GL.VertexAttribPointer (ATTRIB_VERTEX, 2, VertexAttribPointerType.Float, false, 0, squareVertices);
|
|
GL.EnableVertexAttribArray (ATTRIB_VERTEX);
|
|
GL.VertexAttribPointer (ATTRIB_COLOR, 4, VertexAttribPointerType.UnsignedByte, true, 0, squareColors);
|
|
GL.EnableVertexAttribArray (ATTRIB_COLOR);
|
|
|
|
// Validate program before drawing. This is a good check, but only really necessary in a debug build.
|
|
#if DEBUG
|
|
if (!ValidateProgram (program)) {
|
|
Console.WriteLine ("Failed to validate program {0:x}", program);
|
|
return;
|
|
}
|
|
#endif
|
|
} else {
|
|
GL1.MatrixMode (All1.Projection);
|
|
GL1.LoadIdentity ();
|
|
GL1.MatrixMode (All1.Modelview);
|
|
GL1.LoadIdentity ();
|
|
GL1.Translate (0.0f, (float)Math.Sin (transY) / 2.0f, 0.0f);
|
|
transY += 0.075f;
|
|
|
|
GL1.VertexPointer (2, All1.Float, 0, squareVertices);
|
|
GL1.EnableClientState (All1.VertexArray);
|
|
GL1.ColorPointer (4, All1.UnsignedByte, 0, squareColors);
|
|
GL1.EnableClientState (All1.ColorArray);
|
|
}
|
|
|
|
GL.DrawArrays (BeginMode.TriangleStrip, 0, 4);
|
|
|
|
SwapBuffers ();
|
|
}
|
|
|
|
bool LoadShaders ()
|
|
{
|
|
int vertShader, fragShader;
|
|
|
|
// Create shader program.
|
|
program = GL.CreateProgram ();
|
|
|
|
// Create and compile vertex shader.
|
|
var vertShaderPathname = NSBundle.MainBundle.PathForResource ("Shader", "vsh");
|
|
if (!CompileShader (ShaderType.VertexShader, vertShaderPathname, out vertShader)) {
|
|
Console.WriteLine ("Failed to compile vertex shader");
|
|
return false;
|
|
}
|
|
|
|
// Create and compile fragment shader.
|
|
var fragShaderPathname = NSBundle.MainBundle.PathForResource ("Shader", "fsh");
|
|
if (!CompileShader (ShaderType.FragmentShader, fragShaderPathname, out fragShader)) {
|
|
Console.WriteLine ("Failed to compile fragment shader");
|
|
return false;
|
|
}
|
|
|
|
// Attach vertex shader to program.
|
|
GL.AttachShader (program, vertShader);
|
|
|
|
// Attach fragment shader to program.
|
|
GL.AttachShader (program, fragShader);
|
|
|
|
// Bind attribute locations.
|
|
// This needs to be done prior to linking.
|
|
GL.BindAttribLocation (program, ATTRIB_VERTEX, "position");
|
|
GL.BindAttribLocation (program, ATTRIB_COLOR, "color");
|
|
|
|
// Link program.
|
|
if (!LinkProgram (program)) {
|
|
Console.WriteLine ("Failed to link program: {0:x}", program);
|
|
|
|
if (vertShader != 0)
|
|
GL.DeleteShader (vertShader);
|
|
|
|
if (fragShader != 0)
|
|
GL.DeleteShader (fragShader);
|
|
|
|
if (program != 0) {
|
|
GL.DeleteProgram (program);
|
|
program = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Get uniform locations.
|
|
uniforms [UNIFORM_TRANSLATE] = GL.GetUniformLocation (program, "translate");
|
|
|
|
// Release vertex and fragment shaders.
|
|
if (vertShader != 0) {
|
|
GL.DetachShader (program, vertShader);
|
|
GL.DeleteShader (vertShader);
|
|
}
|
|
|
|
if (fragShader != 0) {
|
|
GL.DetachShader (program, fragShader);
|
|
GL.DeleteShader (fragShader);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DestroyShaders ()
|
|
{
|
|
if (program != 0) {
|
|
GL.DeleteProgram (program);
|
|
program = 0;
|
|
}
|
|
}
|
|
|
|
#region Shader utilities
|
|
|
|
static bool CompileShader (ShaderType type, string file, out int shader)
|
|
{
|
|
string src = System.IO.File.ReadAllText (file);
|
|
|
|
shader = GL.CreateShader (type);
|
|
GL.ShaderSource (shader, 1, new string[] { src }, (int[])null);
|
|
GL.CompileShader (shader);
|
|
|
|
#if DEBUG
|
|
int logLength;
|
|
GL.GetShader (shader, ShaderParameter.InfoLogLength, out logLength);
|
|
if (logLength > 0) {
|
|
var infoLog = new System.Text.StringBuilder ();
|
|
GL.GetShaderInfoLog (shader, logLength, out logLength, infoLog);
|
|
Console.WriteLine ("Shader compile log:\n{0}", infoLog);
|
|
}
|
|
#endif
|
|
int status;
|
|
GL.GetShader (shader, ShaderParameter.CompileStatus, out status);
|
|
if (status == 0) {
|
|
GL.DeleteShader (shader);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool LinkProgram (int program)
|
|
{
|
|
GL.LinkProgram (program);
|
|
|
|
#if DEBUG
|
|
int logLength;
|
|
GL.GetProgram (program, ProgramParameter.InfoLogLength, out logLength);
|
|
if (logLength > 0) {
|
|
var infoLog = new System.Text.StringBuilder ();
|
|
GL.GetProgramInfoLog (program, logLength, out logLength, infoLog);
|
|
Console.WriteLine ("Program link log:\n{0}", infoLog);
|
|
}
|
|
#endif
|
|
int status;
|
|
GL.GetProgram (program, ProgramParameter.LinkStatus, out status);
|
|
if (status == 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ValidateProgram (int program)
|
|
{
|
|
GL.ValidateProgram (program);
|
|
|
|
int logLength;
|
|
GL.GetProgram (program, ProgramParameter.InfoLogLength, out logLength);
|
|
if (logLength > 0) {
|
|
var infoLog = new System.Text.StringBuilder ();
|
|
GL.GetProgramInfoLog (program, logLength, out logLength, infoLog);
|
|
Console.WriteLine ("Program validate log:\n{0}", infoLog);
|
|
}
|
|
|
|
int status;
|
|
GL.GetProgram (program, ProgramParameter.LinkStatus, out status);
|
|
if (status == 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|