Opentk/Source/Samples/iOS/ES11/EAGLView.cs
2016-06-08 15:15:59 +09:00

352 lines
11 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 ES11
{
[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 e)
{
System.Diagnostics.Debug.Print(e.ToString());
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
}
}