|
@@ -0,0 +1,288 @@
|
|
|
+using System;
|
|
|
+using System.IO;
|
|
|
+using System.Linq;
|
|
|
+using System.Numerics;
|
|
|
+using System.Runtime.InteropServices;
|
|
|
+using Avalonia;
|
|
|
+using Avalonia.Controls;
|
|
|
+using Avalonia.OpenGL;
|
|
|
+using static Avalonia.OpenGL.GlConsts;
|
|
|
+// ReSharper disable StringLiteralTypo
|
|
|
+
|
|
|
+namespace ControlCatalog.Pages
|
|
|
+{
|
|
|
+ public class OpenGlPage : UserControl
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public class OpenGlPageControl : OpenGlControlBase
|
|
|
+ {
|
|
|
+ private float _yaw;
|
|
|
+
|
|
|
+ public static readonly DirectProperty<OpenGlPageControl, float> YawProperty =
|
|
|
+ AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Yaw", o => o.Yaw, (o, v) => o.Yaw = v);
|
|
|
+
|
|
|
+ public float Yaw
|
|
|
+ {
|
|
|
+ get => _yaw;
|
|
|
+ set => SetAndRaise(YawProperty, ref _yaw, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ private float _pitch;
|
|
|
+
|
|
|
+ public static readonly DirectProperty<OpenGlPageControl, float> PitchProperty =
|
|
|
+ AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Pitch", o => o.Pitch, (o, v) => o.Pitch = v);
|
|
|
+
|
|
|
+ public float Pitch
|
|
|
+ {
|
|
|
+ get => _pitch;
|
|
|
+ set => SetAndRaise(PitchProperty, ref _pitch, value);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private float _roll;
|
|
|
+
|
|
|
+ public static readonly DirectProperty<OpenGlPageControl, float> RollProperty =
|
|
|
+ AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Roll", o => o.Roll, (o, v) => o.Roll = v);
|
|
|
+
|
|
|
+ public float Roll
|
|
|
+ {
|
|
|
+ get => _roll;
|
|
|
+ set => SetAndRaise(RollProperty, ref _roll, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ static OpenGlPageControl()
|
|
|
+ {
|
|
|
+ AffectsRender<OpenGlPageControl>(YawProperty, PitchProperty, RollProperty);
|
|
|
+ }
|
|
|
+
|
|
|
+ private int _vertexShader;
|
|
|
+ private int _fragmentShader;
|
|
|
+ private int _shaderProgram;
|
|
|
+ private int _vertexBufferObject;
|
|
|
+ private int _indexBufferObject;
|
|
|
+ private int _vertexArrayObject;
|
|
|
+
|
|
|
+ private string WithVersion(string shader) =>
|
|
|
+ $"#version {(DisplayType == GlDisplayType.OpenGl ? 120 : 100)}\n" + shader;
|
|
|
+
|
|
|
+ private string WithVersionAndPrecision(string shader, string precision) =>
|
|
|
+ WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision}\n{shader}" : shader);
|
|
|
+
|
|
|
+ private string VertexShaderSource => WithVersion(@"
|
|
|
+ attribute vec3 aPos;
|
|
|
+ attribute vec3 aNormal;
|
|
|
+ uniform mat4 uModel;
|
|
|
+ uniform mat4 uProjection;
|
|
|
+ uniform mat4 uView;
|
|
|
+
|
|
|
+ varying vec3 FragPos;
|
|
|
+ varying vec3 VecPos;
|
|
|
+ varying vec3 Normal;
|
|
|
+ void main()
|
|
|
+ {
|
|
|
+ gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0);
|
|
|
+ FragPos = vec3(uModel * vec4(aPos, 1.0));
|
|
|
+ VecPos = aPos;
|
|
|
+ Normal = normalize(vec3(uModel * vec4(aNormal, 1.0)));
|
|
|
+ }
|
|
|
+");
|
|
|
+
|
|
|
+ private string FragmentShaderSource => WithVersionAndPrecision(@"
|
|
|
+ varying vec3 FragPos;
|
|
|
+ varying vec3 VecPos;
|
|
|
+ varying vec3 Normal;
|
|
|
+ uniform float uMaxY;
|
|
|
+ uniform float uMinY;
|
|
|
+ void main()
|
|
|
+ {
|
|
|
+ float y = (VecPos.y - uMinY) / (uMaxY - uMinY);
|
|
|
+ vec3 objectColor = vec3((1 - y), 0.40 + y / 4, y * 0.75+0.25);
|
|
|
+ //vec3 objectColor = normalize(FragPos);
|
|
|
+
|
|
|
+
|
|
|
+ float ambientStrength = 0.3;
|
|
|
+ vec3 lightColor = vec3(1.0, 1.0, 1.0);
|
|
|
+ vec3 lightPos = vec3(uMaxY * 2, uMaxY * 2, uMaxY * 2);
|
|
|
+ vec3 ambient = ambientStrength * lightColor;
|
|
|
+
|
|
|
+
|
|
|
+ vec3 norm = normalize(Normal);
|
|
|
+ vec3 lightDir = normalize(lightPos - FragPos);
|
|
|
+
|
|
|
+ float diff = max(dot(norm, lightDir), 0.0);
|
|
|
+ vec3 diffuse = diff * lightColor;
|
|
|
+
|
|
|
+ vec3 result = (ambient + diffuse) * objectColor;
|
|
|
+ gl_FragColor = vec4(result, 1.0);
|
|
|
+
|
|
|
+ }
|
|
|
+", "mediump float");
|
|
|
+
|
|
|
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
|
+ private struct Vertex
|
|
|
+ {
|
|
|
+ public Vector3 Position;
|
|
|
+ public Vector3 Normal;
|
|
|
+ }
|
|
|
+
|
|
|
+ private readonly Vertex[] _points;
|
|
|
+ private readonly ushort[] _indices;
|
|
|
+ private readonly float _minY;
|
|
|
+ private readonly float _maxY;
|
|
|
+
|
|
|
+
|
|
|
+ public OpenGlPageControl()
|
|
|
+ {
|
|
|
+ var name = typeof(OpenGlPage).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin"));
|
|
|
+ using (var sr = new BinaryReader(typeof(OpenGlPage).Assembly.GetManifestResourceStream(name)))
|
|
|
+ {
|
|
|
+ var buf = new byte[sr.ReadInt32()];
|
|
|
+ sr.Read(buf, 0, buf.Length);
|
|
|
+ var points = new float[buf.Length / 4];
|
|
|
+ Buffer.BlockCopy(buf, 0, points, 0, buf.Length);
|
|
|
+ buf = new byte[sr.ReadInt32()];
|
|
|
+ sr.Read(buf, 0, buf.Length);
|
|
|
+ _indices = new ushort[buf.Length / 2];
|
|
|
+ Buffer.BlockCopy(buf, 0, _indices, 0, buf.Length);
|
|
|
+ _points = new Vertex[points.Length / 3];
|
|
|
+ for (var primitive = 0; primitive < points.Length / 3; primitive++)
|
|
|
+ {
|
|
|
+ var srci = primitive * 3;
|
|
|
+ _points[primitive] = new Vertex
|
|
|
+ {
|
|
|
+ Position = new Vector3(points[srci], points[srci + 1], points[srci + 2])
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < _indices.Length; i += 3)
|
|
|
+ {
|
|
|
+ Vector3 a = _points[_indices[i]].Position;
|
|
|
+ Vector3 b = _points[_indices[i + 1]].Position;
|
|
|
+ Vector3 c = _points[_indices[i + 2]].Position;
|
|
|
+ var normal = Vector3.Normalize(Vector3.Cross(c - b, a - b));
|
|
|
+
|
|
|
+ _points[_indices[i]].Normal += normal;
|
|
|
+ _points[_indices[i + 1]].Normal += normal;
|
|
|
+ _points[_indices[i + 2]].Normal += normal;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < _points.Length; i++)
|
|
|
+ {
|
|
|
+ _points[i].Normal = Vector3.Normalize(_points[i].Normal);
|
|
|
+ _maxY = Math.Max(_maxY, _points[i].Position.Y);
|
|
|
+ _minY = Math.Min(_minY, _points[i].Position.Y);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void CheckError(GlInterface gl)
|
|
|
+ {
|
|
|
+ int err;
|
|
|
+ while ((err = gl.GetError()) != GL_NO_ERROR)
|
|
|
+ Console.WriteLine(err);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected unsafe override void OnOpenGlInit(GlInterface GL, int fb)
|
|
|
+ {
|
|
|
+ // Load the source of the vertex shader and compile it.
|
|
|
+ _vertexShader = GL.CreateShader(GL_VERTEX_SHADER);
|
|
|
+ Console.WriteLine(GL.CompileShaderAndGetError(_vertexShader, VertexShaderSource));
|
|
|
+
|
|
|
+ // Load the source of the fragment shader and compile it.
|
|
|
+ _fragmentShader = GL.CreateShader(GL_FRAGMENT_SHADER);
|
|
|
+ Console.WriteLine(GL.CompileShaderAndGetError(_fragmentShader, FragmentShaderSource));
|
|
|
+
|
|
|
+ // Create the shader program, attach the vertex and fragment shaders and link the program.
|
|
|
+ _shaderProgram = GL.CreateProgram();
|
|
|
+ GL.AttachShader(_shaderProgram, _vertexShader);
|
|
|
+ GL.AttachShader(_shaderProgram, _fragmentShader);
|
|
|
+ const int positionLocation = 0;
|
|
|
+ const int normalLocation = 1;
|
|
|
+ GL.BindAttribLocationString(_shaderProgram, positionLocation, "aPos");
|
|
|
+ GL.BindAttribLocationString(_shaderProgram, normalLocation, "aNormal");
|
|
|
+ Console.WriteLine(GL.LinkProgramAndGetError(_shaderProgram));
|
|
|
+ CheckError(GL);
|
|
|
+
|
|
|
+ // Create the vertex buffer object (VBO) for the vertex data.
|
|
|
+ _vertexBufferObject = GL.GenBuffer();
|
|
|
+ // Bind the VBO and copy the vertex data into it.
|
|
|
+ GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
|
|
|
+ var vertexSize = Marshal.SizeOf<Vertex>();
|
|
|
+ fixed (void* pdata = _points)
|
|
|
+ GL.BufferData(GL_ARRAY_BUFFER, new IntPtr(_points.Length * vertexSize),
|
|
|
+ new IntPtr(pdata), GL_STATIC_DRAW);
|
|
|
+
|
|
|
+ _indexBufferObject = GL.GenBuffer();
|
|
|
+ GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject);
|
|
|
+ fixed (void* pdata = _indices)
|
|
|
+ GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata),
|
|
|
+ GL_STATIC_DRAW);
|
|
|
+
|
|
|
+ _vertexArrayObject = GL.GenVertexArray();
|
|
|
+ GL.BindVertexArray(_vertexArrayObject);
|
|
|
+
|
|
|
+ GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT,
|
|
|
+ 0, vertexSize, IntPtr.Zero);
|
|
|
+ GL.VertexAttribPointer(normalLocation, 3, GL_FLOAT,
|
|
|
+ 0, vertexSize, new IntPtr(12));
|
|
|
+ GL.EnableVertexAttribArray(positionLocation);
|
|
|
+ GL.EnableVertexAttribArray(normalLocation);
|
|
|
+ CheckError(GL);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override void OnOpenGlDeinit(GlInterface GL, int fb)
|
|
|
+ {
|
|
|
+ // Unbind everything
|
|
|
+ GL.BindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
+ GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
+ GL.BindVertexArray(0);
|
|
|
+ GL.UseProgram(0);
|
|
|
+
|
|
|
+ // Delete all resources.
|
|
|
+ GL.DeleteBuffer(_vertexBufferObject);
|
|
|
+ GL.DeleteBuffer(_indexBufferObject);
|
|
|
+ GL.DeleteVertexArray(_vertexArrayObject);
|
|
|
+ GL.DeleteProgram(_shaderProgram);
|
|
|
+ GL.DeleteShader(_fragmentShader);
|
|
|
+ GL.DeleteShader(_vertexShader);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override unsafe void OnOpenGlRender(GlInterface gl, int fb)
|
|
|
+ {
|
|
|
+ gl.ClearColor(0, 0, 0, 0);
|
|
|
+ gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
+ gl.Enable(GL_DEPTH_TEST);
|
|
|
+ gl.Viewport(0, 0, (int)Bounds.Width, (int)Bounds.Height);
|
|
|
+ var GL = gl;
|
|
|
+
|
|
|
+ GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
|
|
|
+ GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject);
|
|
|
+ GL.BindVertexArray(_vertexArrayObject);
|
|
|
+ GL.UseProgram(_shaderProgram);
|
|
|
+ var projection =
|
|
|
+ Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(Bounds.Width / Bounds.Height),
|
|
|
+ 0.01f, 1000);
|
|
|
+
|
|
|
+
|
|
|
+ var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0));
|
|
|
+ var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll);
|
|
|
+ var modelLoc = GL.GetUniformLocationString(_shaderProgram, "uModel");
|
|
|
+ var viewLoc = GL.GetUniformLocationString(_shaderProgram, "uView");
|
|
|
+ var projectionLoc = GL.GetUniformLocationString(_shaderProgram, "uProjection");
|
|
|
+ var maxYLoc = GL.GetUniformLocationString(_shaderProgram, "uMaxY");
|
|
|
+ var minYLoc = GL.GetUniformLocationString(_shaderProgram, "uMinY");
|
|
|
+ GL.UniformMatrix4fv(modelLoc, 1, false, &model);
|
|
|
+ GL.UniformMatrix4fv(viewLoc, 1, false, &view);
|
|
|
+ GL.UniformMatrix4fv(projectionLoc, 1, false, &projection);
|
|
|
+ GL.Uniform1f(maxYLoc, _maxY);
|
|
|
+ GL.Uniform1f(minYLoc, _minY);
|
|
|
+ GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero);
|
|
|
+
|
|
|
+ CheckError(GL);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|