Browse Source
* OpenGL via API lease example * Hide glClearDepth internals * Missing filepull/15722/head
committed by
GitHub
15 changed files with 875 additions and 426 deletions
@ -0,0 +1,30 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Pages.OpenGl.GlPageKnobs" |
|||
xmlns:pages="using:ControlCatalog.Pages" |
|||
xmlns:gl="using:ControlCatalog.Pages.OpenGl" |
|||
> |
|||
<Grid> |
|||
<StackPanel> |
|||
<TextBlock Text="{Binding $parent[gl:GlPageKnobs].Info}"/> |
|||
</StackPanel> |
|||
<Grid ColumnDefinitions="*,Auto" Margin="20"> |
|||
<StackPanel Grid.Column="1" MinWidth="300"> |
|||
<TextBlock>Yaw</TextBlock> |
|||
<Slider Value="{Binding $parent[gl:GlPageKnobs].Yaw, Mode=TwoWay}" Maximum="10"/> |
|||
<TextBlock>Pitch</TextBlock> |
|||
<Slider Value="{Binding $parent[gl:GlPageKnobs].Pitch, Mode=TwoWay}" Maximum="10"/> |
|||
<TextBlock>Roll</TextBlock> |
|||
<Slider Value="{Binding $parent[gl:GlPageKnobs].Roll, Mode=TwoWay}" Maximum="10"/> |
|||
<StackPanel Orientation="Horizontal"> |
|||
<TextBlock FontWeight="Bold" Foreground="#C000C0">D</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#00C090">I</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#90C000">S</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#C09000">C</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#00C090">O</TextBlock> |
|||
</StackPanel> |
|||
<Slider Value="{Binding $parent[gl:GlPageKnobs].Disco, Mode=TwoWay}" Maximum="1"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
</Grid> |
|||
</UserControl> |
|||
@ -0,0 +1,70 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace ControlCatalog.Pages.OpenGl; |
|||
|
|||
public partial class GlPageKnobs : UserControl |
|||
{ |
|||
public GlPageKnobs() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
private float _yaw; |
|||
|
|||
public static readonly DirectProperty<GlPageKnobs, float> YawProperty = |
|||
AvaloniaProperty.RegisterDirect<GlPageKnobs, 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<GlPageKnobs, float> PitchProperty = |
|||
AvaloniaProperty.RegisterDirect<GlPageKnobs, 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<GlPageKnobs, float> RollProperty = |
|||
AvaloniaProperty.RegisterDirect<GlPageKnobs, float>("Roll", o => o.Roll, (o, v) => o.Roll = v); |
|||
|
|||
public float Roll |
|||
{ |
|||
get => _roll; |
|||
set => SetAndRaise(RollProperty, ref _roll, value); |
|||
} |
|||
|
|||
|
|||
private float _disco; |
|||
|
|||
public static readonly DirectProperty<GlPageKnobs, float> DiscoProperty = |
|||
AvaloniaProperty.RegisterDirect<GlPageKnobs, float>("Disco", o => o.Disco, (o, v) => o.Disco = v); |
|||
|
|||
public float Disco |
|||
{ |
|||
get => _disco; |
|||
set => SetAndRaise(DiscoProperty, ref _disco, value); |
|||
} |
|||
|
|||
private string _info = string.Empty; |
|||
|
|||
public static readonly DirectProperty<GlPageKnobs, string> InfoProperty = |
|||
AvaloniaProperty.RegisterDirect<GlPageKnobs, string>("Info", o => o.Info, (o, v) => o.Info = v); |
|||
|
|||
public string Info |
|||
{ |
|||
get => _info; |
|||
private set => SetAndRaise(InfoProperty, ref _info, value); |
|||
} |
|||
} |
|||
@ -0,0 +1,311 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Numerics; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia; |
|||
using Avalonia.OpenGL; |
|||
using static Avalonia.OpenGL.GlConsts; |
|||
// ReSharper disable StringLiteralTypo
|
|||
|
|||
namespace ControlCatalog.Pages.OpenGl; |
|||
|
|||
internal class OpenGlContent |
|||
{ |
|||
private int _vertexShader; |
|||
private int _fragmentShader; |
|||
private int _shaderProgram; |
|||
private int _vertexBufferObject; |
|||
private int _indexBufferObject; |
|||
private int _vertexArrayObject; |
|||
private GlVersion GlVersion; |
|||
|
|||
private string GetShader(bool fragment, string shader) |
|||
{ |
|||
var version = (GlVersion.Type == GlProfileType.OpenGL |
|||
? RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 150 : 120 |
|||
: 100); |
|||
var data = "#version " + version + "\n"; |
|||
if (GlVersion.Type == GlProfileType.OpenGLES) |
|||
data += "precision mediump float;\n"; |
|||
if (version >= 150) |
|||
{ |
|||
shader = shader.Replace("attribute", "in"); |
|||
if (fragment) |
|||
shader = shader |
|||
.Replace("varying", "in") |
|||
.Replace("//DECLAREGLFRAG", "out vec4 outFragColor;") |
|||
.Replace("gl_FragColor", "outFragColor"); |
|||
else |
|||
shader = shader.Replace("varying", "out"); |
|||
} |
|||
|
|||
data += shader; |
|||
|
|||
return data; |
|||
} |
|||
|
|||
|
|||
private string VertexShaderSource => GetShader(false, @"
|
|||
attribute vec3 aPos; |
|||
attribute vec3 aNormal; |
|||
uniform mat4 uModel; |
|||
uniform mat4 uProjection; |
|||
uniform mat4 uView; |
|||
|
|||
varying vec3 FragPos; |
|||
varying vec3 VecPos; |
|||
varying vec3 Normal; |
|||
uniform float uTime; |
|||
uniform float uDisco; |
|||
void main() |
|||
{ |
|||
float discoScale = sin(uTime * 10.0) / 10.0; |
|||
float distortionX = 1.0 + uDisco * cos(uTime * 20.0) / 10.0; |
|||
|
|||
float scale = 1.0 + uDisco * discoScale; |
|||
|
|||
vec3 scaledPos = aPos; |
|||
scaledPos.x = scaledPos.x * distortionX; |
|||
|
|||
scaledPos *= scale; |
|||
gl_Position = uProjection * uView * uModel * vec4(scaledPos, 1.0); |
|||
FragPos = vec3(uModel * vec4(aPos, 1.0)); |
|||
VecPos = aPos; |
|||
Normal = normalize(vec3(uModel * vec4(aNormal, 1.0))); |
|||
} |
|||
");
|
|||
|
|||
private string FragmentShaderSource => GetShader(true, @"
|
|||
varying vec3 FragPos; |
|||
varying vec3 VecPos; |
|||
varying vec3 Normal; |
|||
uniform float uMaxY; |
|||
uniform float uMinY; |
|||
uniform float uTime; |
|||
uniform float uDisco; |
|||
//DECLAREGLFRAG
|
|||
|
|||
void main() |
|||
{ |
|||
float y = (VecPos.y - uMinY) / (uMaxY - uMinY); |
|||
float c = cos(atan(VecPos.x, VecPos.z) * 20.0 + uTime * 40.0 + y * 50.0); |
|||
float s = sin(-atan(VecPos.z, VecPos.x) * 20.0 - uTime * 20.0 - y * 30.0); |
|||
|
|||
vec3 discoColor = vec3( |
|||
0.5 + abs(0.5 - y) * cos(uTime * 10.0), |
|||
0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c / 4.0)), |
|||
0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s / 4.0)))); |
|||
|
|||
vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75 + 0.25); |
|||
objectColor = objectColor * (1.0 - uDisco) + discoColor * uDisco; |
|||
|
|||
float ambientStrength = 0.3; |
|||
vec3 lightColor = vec3(1.0, 1.0, 1.0); |
|||
vec3 lightPos = vec3(uMaxY * 2.0, uMaxY * 2.0, uMaxY * 2.0); |
|||
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); |
|||
|
|||
} |
|||
");
|
|||
|
|||
[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 OpenGlContent() |
|||
{ |
|||
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 static void CheckError(GlInterface gl) |
|||
{ |
|||
int err; |
|||
while ((err = gl.GetError()) != GL_NO_ERROR) |
|||
Console.WriteLine(err); |
|||
} |
|||
|
|||
public string Info { get; private set; } |
|||
|
|||
public unsafe void Init(GlInterface GL, GlVersion version) |
|||
{ |
|||
GlVersion = version; |
|||
CheckError(GL); |
|||
|
|||
Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}"; |
|||
|
|||
// 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); |
|||
CheckError(GL); |
|||
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); |
|||
CheckError(GL); |
|||
fixed (void* pdata = _indices) |
|||
GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata), |
|||
GL_STATIC_DRAW); |
|||
CheckError(GL); |
|||
_vertexArrayObject = GL.GenVertexArray(); |
|||
GL.BindVertexArray(_vertexArrayObject); |
|||
CheckError(GL); |
|||
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); |
|||
|
|||
} |
|||
|
|||
public void Deinit(GlInterface GL) |
|||
{ |
|||
// 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); |
|||
} |
|||
|
|||
static Stopwatch St = Stopwatch.StartNew(); |
|||
|
|||
public unsafe void OnOpenGlRender(GlInterface gl, int fb, PixelSize size, |
|||
float yaw, float pitch, float roll, float disco) |
|||
{ |
|||
gl.Viewport(0, 0, size.Width, size.Height); |
|||
gl.ClearDepth(1); |
|||
gl.Disable(GL_CULL_FACE); |
|||
gl.Disable(GL_SCISSOR_TEST); |
|||
gl.DepthFunc(GL_LESS); |
|||
gl.DepthMask(1); |
|||
|
|||
gl.ClearColor(0, 0, 0, 0); |
|||
gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|||
gl.Enable(GL_DEPTH_TEST); |
|||
|
|||
|
|||
var GL = gl; |
|||
|
|||
GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); |
|||
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); |
|||
GL.BindVertexArray(_vertexArrayObject); |
|||
GL.UseProgram(_shaderProgram); |
|||
CheckError(GL); |
|||
var projection = |
|||
Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(size.Width / size.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"); |
|||
var timeLoc = GL.GetUniformLocationString(_shaderProgram, "uTime"); |
|||
var discoLoc = GL.GetUniformLocationString(_shaderProgram, "uDisco"); |
|||
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.Uniform1f(timeLoc, (float)St.Elapsed.TotalSeconds); |
|||
GL.Uniform1f(discoLoc, disco); |
|||
CheckError(GL); |
|||
GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero); |
|||
|
|||
CheckError(GL); |
|||
} |
|||
} |
|||
@ -0,0 +1,118 @@ |
|||
using System; |
|||
using Avalonia; |
|||
using Avalonia.Logging; |
|||
using Avalonia.OpenGL; |
|||
using SkiaSharp; |
|||
using static Avalonia.OpenGL.GlConsts; |
|||
namespace ControlCatalog.Pages.OpenGl; |
|||
|
|||
internal class OpenGlFbo : IDisposable |
|||
{ |
|||
private readonly GRContext _grContext; |
|||
private int _fbo; |
|||
private int _depthBuffer; |
|||
private int _texture; |
|||
private PixelSize _size; |
|||
public PixelSize Size => _size; |
|||
public GlInterface Gl => Context.GlInterface; |
|||
public IGlContext Context { get; } |
|||
|
|||
public OpenGlFbo(IGlContext context, GRContext grContext) |
|||
{ |
|||
_grContext = grContext; |
|||
Context = context; |
|||
_fbo = Gl.GenFramebuffer(); |
|||
} |
|||
|
|||
public void Resize(PixelSize size) |
|||
{ |
|||
if(_size == size) |
|||
return; |
|||
|
|||
if (_texture != 0) |
|||
Gl.DeleteTexture(_texture); |
|||
_texture = 0; |
|||
if(_depthBuffer != 0) |
|||
Gl.DeleteRenderbuffer(_depthBuffer); |
|||
_depthBuffer = 0; |
|||
Gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo); |
|||
|
|||
_texture = Gl.GenTexture(); |
|||
|
|||
var textureFormat = Context.Version.Type == GlProfileType.OpenGLES && Context.Version.Major == 2 |
|||
? GL_RGBA |
|||
: GL_RGBA8; |
|||
|
|||
Gl.BindTexture(GL_TEXTURE_2D, _texture); |
|||
Gl.TexImage2D(GL_TEXTURE_2D, 0, textureFormat, size.Width, size.Height, 0, GL_RGBA, |
|||
GL_UNSIGNED_BYTE, IntPtr.Zero); |
|||
Gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); |
|||
|
|||
_depthBuffer = Gl.GenRenderbuffer(); |
|||
var depthFormat = Context.Version.Type == GlProfileType.OpenGLES |
|||
? GL_DEPTH_COMPONENT16 |
|||
: GL_DEPTH_COMPONENT; |
|||
Gl.BindRenderbuffer(GL_RENDERBUFFER, _depthBuffer); |
|||
Gl.RenderbufferStorage(GL_RENDERBUFFER, depthFormat, size.Width, size.Height); |
|||
Gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer); |
|||
|
|||
var status = Gl.CheckFramebufferStatus(GL_FRAMEBUFFER); |
|||
IsValid = (status == GL_FRAMEBUFFER_COMPLETE); |
|||
if(!IsValid) |
|||
{ |
|||
int code = Gl.GetError(); |
|||
Console.WriteLine("Unable to configure OpenGL FBO: " + code); |
|||
} |
|||
|
|||
_size = size; |
|||
} |
|||
|
|||
public bool IsValid { get; private set; } |
|||
|
|||
public int Fbo => _fbo; |
|||
|
|||
public SKImage? Snapshot() |
|||
{ |
|||
Gl.Flush(); |
|||
_grContext.ResetContext(); |
|||
|
|||
using var texture = new GRBackendTexture(_size.Width, _size.Height, false, |
|||
new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)_texture, SKColorType.Rgba8888.ToGlSizedFormat())); |
|||
|
|||
var surf = SKSurface.Create(_grContext, texture, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888); |
|||
if (surf == null) |
|||
{ |
|||
using var unformatted = new GRBackendTexture(_size.Width, _size.Height, false, |
|||
new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)_texture)); |
|||
|
|||
surf = SKSurface.Create(_grContext, unformatted, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888); |
|||
} |
|||
|
|||
SKImage? rv; |
|||
using (surf) |
|||
rv = surf?.Snapshot(); |
|||
_grContext.Flush(); |
|||
return rv; |
|||
/* |
|||
var target = new GRBackendRenderTarget(_size.Width, _size.Height, 0, 0, |
|||
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat())); |
|||
SKImage rv; |
|||
using (var surface = SKSurface.Create(_grContext, target, |
|||
GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888, |
|||
new SKSurfaceProperties(SKPixelGeometry.RgbHorizontal))) |
|||
rv = surface.Snapshot(); |
|||
_grContext.Flush(); |
|||
return rv;*/ |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if(_fbo != 0) |
|||
Gl.DeleteFramebuffer(_fbo); |
|||
_fbo = 0; |
|||
if (_depthBuffer != 0) |
|||
Gl.DeleteRenderbuffer(_depthBuffer); |
|||
if(_texture != 0) |
|||
Gl.DeleteTexture(_texture); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Pages.OpenGlLeasePage" |
|||
xmlns:pages="using:ControlCatalog.Pages" |
|||
xmlns:openGl="clr-namespace:ControlCatalog.Pages.OpenGl"> |
|||
<Grid> |
|||
<Control x:Name="Viewport" /> |
|||
<openGl:GlPageKnobs x:Name="Knobs" /> |
|||
</Grid> |
|||
</UserControl> |
|||
@ -0,0 +1,216 @@ |
|||
using System; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.LogicalTree; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.Media; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering.Composition; |
|||
using Avalonia.Skia; |
|||
using ControlCatalog.Pages.OpenGl; |
|||
using SkiaSharp; |
|||
using static Avalonia.OpenGL.GlConsts; |
|||
|
|||
namespace ControlCatalog.Pages; |
|||
|
|||
public class OpenGlLeasePage : UserControl |
|||
{ |
|||
private readonly Control _viewport; |
|||
private readonly GlPageKnobs _knobs; |
|||
private CompositionCustomVisual? _visual; |
|||
|
|||
class GlVisual : CompositionCustomVisualHandler |
|||
{ |
|||
private OpenGlContent _content; |
|||
private Parameters _parameters; |
|||
private bool _contentInitialized; |
|||
private OpenGlFbo? _fbo; |
|||
private bool _reRender; |
|||
private IGlContext? _gl; |
|||
|
|||
public GlVisual(OpenGlContent content, Parameters parameters) |
|||
{ |
|||
_content = content; |
|||
_parameters = parameters; |
|||
} |
|||
|
|||
public override void OnRender(ImmediateDrawingContext drawingContext) |
|||
{ |
|||
if (_parameters.Disco > 0.01f) |
|||
RegisterForNextAnimationFrameUpdate(); |
|||
var bounds = GetRenderBounds(); |
|||
var size = PixelSize.FromSize(bounds.Size, 1); |
|||
if (size.Width < 1 || size.Height < 1) |
|||
return; |
|||
|
|||
if(drawingContext.TryGetFeature<ISkiaSharpApiLeaseFeature>(out var skiaFeature)) |
|||
{ |
|||
using var skiaLease = skiaFeature.Lease(); |
|||
var grContext = skiaLease.GrContext; |
|||
if (grContext == null) |
|||
return; |
|||
SKImage? snapshot; |
|||
using (var platformApiLease = skiaLease.TryLeasePlatformGraphicsApi()) |
|||
{ |
|||
if (platformApiLease?.Context is not IGlContext glContext) |
|||
return; |
|||
|
|||
var gl = glContext.GlInterface; |
|||
if (_gl != glContext) |
|||
{ |
|||
// The old context is lost
|
|||
_fbo = null; |
|||
_contentInitialized = false; |
|||
_gl = glContext; |
|||
} |
|||
|
|||
gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFb); |
|||
|
|||
_fbo ??= new OpenGlFbo(glContext, grContext); |
|||
if (_fbo.Size != size) |
|||
_fbo.Resize(size); |
|||
|
|||
gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo.Fbo); |
|||
|
|||
|
|||
if (!_contentInitialized) |
|||
{ |
|||
_content.Init(gl, glContext.Version); |
|||
_contentInitialized = true; |
|||
} |
|||
|
|||
|
|||
|
|||
_content.OnOpenGlRender(gl, _fbo.Fbo, size, _parameters.Yaw, _parameters.Pitch, |
|||
_parameters.Roll, _parameters.Disco); |
|||
|
|||
snapshot = _fbo.Snapshot(); |
|||
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFb); |
|||
} |
|||
|
|||
using(snapshot) |
|||
if (snapshot != null) |
|||
skiaLease.SkCanvas.DrawImage(snapshot, new SKRect(0, 0, |
|||
(float)bounds.Width, (float)bounds.Height)); |
|||
} |
|||
} |
|||
|
|||
public override void OnAnimationFrameUpdate() |
|||
{ |
|||
if (_reRender || _parameters.Disco > 0.01f) |
|||
{ |
|||
_reRender = false; |
|||
Invalidate(); |
|||
} |
|||
|
|||
base.OnAnimationFrameUpdate(); |
|||
} |
|||
|
|||
public override void OnMessage(object message) |
|||
{ |
|||
if (message is Parameters p) |
|||
{ |
|||
_parameters = p; |
|||
_reRender = true; |
|||
RegisterForNextAnimationFrameUpdate(); |
|||
} |
|||
else if (message is DisposeMessage) |
|||
{ |
|||
if (_gl != null) |
|||
{ |
|||
try |
|||
{ |
|||
if (_fbo != null || _contentInitialized) |
|||
{ |
|||
using (_gl.MakeCurrent()) |
|||
{ |
|||
if (_contentInitialized) |
|||
_content.Deinit(_gl.GlInterface); |
|||
_contentInitialized = false; |
|||
_fbo?.Dispose(); |
|||
_fbo = null; |
|||
} |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Console.WriteLine(e.ToString()); |
|||
} |
|||
|
|||
_gl = null; |
|||
} |
|||
} |
|||
|
|||
base.OnMessage(message); |
|||
} |
|||
} |
|||
|
|||
public class Parameters |
|||
{ |
|||
public float Yaw; |
|||
public float Pitch; |
|||
public float Roll; |
|||
public float Disco; |
|||
} |
|||
|
|||
public class DisposeMessage |
|||
{ |
|||
|
|||
} |
|||
|
|||
public OpenGlLeasePage() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
_viewport = this.FindControl<Control>("Viewport")!; |
|||
_viewport.AttachedToVisualTree += ViewportAttachedToVisualTree; |
|||
_viewport.DetachedFromVisualTree += ViewportDetachedFromVisualTree; |
|||
_knobs = this.FindControl<GlPageKnobs>("Knobs")!; |
|||
_knobs.PropertyChanged += KnobsPropertyChanged; |
|||
} |
|||
|
|||
private void KnobsPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs change) |
|||
{ |
|||
if (change.Property == GlPageKnobs.YawProperty |
|||
|| change.Property == GlPageKnobs.RollProperty |
|||
|| change.Property == GlPageKnobs.PitchProperty |
|||
|| change.Property == GlPageKnobs.DiscoProperty) |
|||
_visual?.SendHandlerMessage(GetParameters()); |
|||
} |
|||
|
|||
Parameters GetParameters() => new() |
|||
{ |
|||
Yaw = _knobs!.Yaw, Pitch = _knobs.Pitch, Roll = _knobs.Roll, Disco = _knobs.Disco |
|||
}; |
|||
|
|||
private void ViewportAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) |
|||
{ |
|||
var visual = ElementComposition.GetElementVisual(_viewport!); |
|||
if(visual == null) |
|||
return; |
|||
_visual = visual.Compositor.CreateCustomVisual(new GlVisual(new OpenGlContent(), GetParameters())); |
|||
ElementComposition.SetElementChildVisual(_viewport, _visual); |
|||
UpdateSize(Bounds.Size); |
|||
} |
|||
|
|||
private void UpdateSize(Size size) |
|||
{ |
|||
if (_visual != null) |
|||
_visual.Size = new Vector(size.Width, size.Height); |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
var size = base.ArrangeOverride(finalSize); |
|||
UpdateSize(size); |
|||
return size; |
|||
} |
|||
|
|||
private void ViewportDetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) |
|||
{ |
|||
_visual?.SendHandlerMessage(new DisposeMessage()); |
|||
_visual = null; |
|||
ElementComposition.SetElementChildVisual(_viewport, null); |
|||
base.OnDetachedFromVisualTree(e); |
|||
} |
|||
} |
|||
@ -1,29 +1,10 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ControlCatalog.Pages.OpenGlPage" |
|||
xmlns:pages="using:ControlCatalog.Pages"> |
|||
xmlns:pages="using:ControlCatalog.Pages" |
|||
xmlns:openGl="clr-namespace:ControlCatalog.Pages.OpenGl"> |
|||
<Grid> |
|||
<pages:OpenGlPageControl x:Name="GL"/> |
|||
<StackPanel> |
|||
<TextBlock Text="{Binding #GL.Info}"/> |
|||
</StackPanel> |
|||
<Grid ColumnDefinitions="*,Auto" Margin="20"> |
|||
<StackPanel Grid.Column="1" MinWidth="300"> |
|||
<TextBlock>Yaw</TextBlock> |
|||
<Slider Value="{Binding Yaw, Mode=TwoWay, ElementName=GL}" Maximum="10"/> |
|||
<TextBlock>Pitch</TextBlock> |
|||
<Slider Value="{Binding Pitch, Mode=TwoWay, ElementName=GL}" Maximum="10"/> |
|||
<TextBlock>Roll</TextBlock> |
|||
<Slider Value="{Binding Roll, Mode=TwoWay, ElementName=GL}" Maximum="10"/> |
|||
<StackPanel Orientation="Horizontal"> |
|||
<TextBlock FontWeight="Bold" Foreground="#C000C0">D</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#00C090">I</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#90C000">S</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#C09000">C</TextBlock> |
|||
<TextBlock FontWeight="Bold" Foreground="#00C090">O</TextBlock> |
|||
</StackPanel> |
|||
<Slider Value="{Binding Disco, Mode=TwoWay, ElementName=GL}" Maximum="1"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
<openGl:GlPageKnobs x:Name="Knobs" /> |
|||
</Grid> |
|||
</UserControl> |
|||
|
|||
@ -1,375 +1,56 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Numerics; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.OpenGL.Controls; |
|||
using Avalonia.Platform.Interop; |
|||
using Avalonia.Threading; |
|||
using static Avalonia.OpenGL.GlConsts; |
|||
using ControlCatalog.Pages.OpenGl; |
|||
|
|||
// ReSharper disable StringLiteralTypo
|
|||
|
|||
namespace ControlCatalog.Pages |
|||
{ |
|||
public class OpenGlPage : UserControl |
|||
{ |
|||
|
|||
public OpenGlPage() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
this.FindControl<OpenGlPageControl>("GL") |
|||
!.Init(this.FindControl<GlPageKnobs>("Knobs")!); |
|||
} |
|||
} |
|||
|
|||
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); |
|||
private OpenGlContent _content = new(); |
|||
private GlPageKnobs? _knobs; |
|||
|
|||
public float Yaw |
|||
public void Init(GlPageKnobs knobs) |
|||
{ |
|||
get => _yaw; |
|||
set => SetAndRaise(YawProperty, ref _yaw, value); |
|||
_knobs = knobs; |
|||
_knobs.PropertyChanged += KnobsPropertyChanged; |
|||
} |
|||
|
|||
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 |
|||
private void KnobsPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs change) |
|||
{ |
|||
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); |
|||
} |
|||
|
|||
|
|||
private float _disco; |
|||
|
|||
public static readonly DirectProperty<OpenGlPageControl, float> DiscoProperty = |
|||
AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Disco", o => o.Disco, (o, v) => o.Disco = v); |
|||
|
|||
public float Disco |
|||
{ |
|||
get => _disco; |
|||
set => SetAndRaise(DiscoProperty, ref _disco, value); |
|||
} |
|||
|
|||
private string _info = string.Empty; |
|||
|
|||
public static readonly DirectProperty<OpenGlPageControl, string> InfoProperty = |
|||
AvaloniaProperty.RegisterDirect<OpenGlPageControl, string>("Info", o => o.Info, (o, v) => o.Info = v); |
|||
|
|||
public string Info |
|||
{ |
|||
get => _info; |
|||
private set => SetAndRaise(InfoProperty, ref _info, value); |
|||
if (change.Property == GlPageKnobs.YawProperty |
|||
|| change.Property == GlPageKnobs.RollProperty |
|||
|| change.Property == GlPageKnobs.PitchProperty |
|||
|| change.Property == GlPageKnobs.DiscoProperty) |
|||
RequestNextFrameRendering(); |
|||
} |
|||
|
|||
private int _vertexShader; |
|||
private int _fragmentShader; |
|||
private int _shaderProgram; |
|||
private int _vertexBufferObject; |
|||
private int _indexBufferObject; |
|||
private int _vertexArrayObject; |
|||
|
|||
private string GetShader(bool fragment, string shader) |
|||
{ |
|||
var version = (GlVersion.Type == GlProfileType.OpenGL ? |
|||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 150 : 120 : |
|||
100); |
|||
var data = "#version " + version + "\n"; |
|||
if (GlVersion.Type == GlProfileType.OpenGLES) |
|||
data += "precision mediump float;\n"; |
|||
if (version >= 150) |
|||
{ |
|||
shader = shader.Replace("attribute", "in"); |
|||
if (fragment) |
|||
shader = shader |
|||
.Replace("varying", "in") |
|||
.Replace("//DECLAREGLFRAG", "out vec4 outFragColor;") |
|||
.Replace("gl_FragColor", "outFragColor"); |
|||
else |
|||
shader = shader.Replace("varying", "out"); |
|||
} |
|||
|
|||
data += shader; |
|||
|
|||
return data; |
|||
} |
|||
|
|||
|
|||
private string VertexShaderSource => GetShader(false, @"
|
|||
attribute vec3 aPos; |
|||
attribute vec3 aNormal; |
|||
uniform mat4 uModel; |
|||
uniform mat4 uProjection; |
|||
uniform mat4 uView; |
|||
|
|||
varying vec3 FragPos; |
|||
varying vec3 VecPos; |
|||
varying vec3 Normal; |
|||
uniform float uTime; |
|||
uniform float uDisco; |
|||
void main() |
|||
{ |
|||
float discoScale = sin(uTime * 10.0) / 10.0; |
|||
float distortionX = 1.0 + uDisco * cos(uTime * 20.0) / 10.0; |
|||
|
|||
float scale = 1.0 + uDisco * discoScale; |
|||
|
|||
vec3 scaledPos = aPos; |
|||
scaledPos.x = scaledPos.x * distortionX; |
|||
|
|||
scaledPos *= scale; |
|||
gl_Position = uProjection * uView * uModel * vec4(scaledPos, 1.0); |
|||
FragPos = vec3(uModel * vec4(aPos, 1.0)); |
|||
VecPos = aPos; |
|||
Normal = normalize(vec3(uModel * vec4(aNormal, 1.0))); |
|||
} |
|||
");
|
|||
|
|||
private string FragmentShaderSource => GetShader(true, @"
|
|||
varying vec3 FragPos; |
|||
varying vec3 VecPos; |
|||
varying vec3 Normal; |
|||
uniform float uMaxY; |
|||
uniform float uMinY; |
|||
uniform float uTime; |
|||
uniform float uDisco; |
|||
//DECLAREGLFRAG
|
|||
|
|||
void main() |
|||
{ |
|||
float y = (VecPos.y - uMinY) / (uMaxY - uMinY); |
|||
float c = cos(atan(VecPos.x, VecPos.z) * 20.0 + uTime * 40.0 + y * 50.0); |
|||
float s = sin(-atan(VecPos.z, VecPos.x) * 20.0 - uTime * 20.0 - y * 30.0); |
|||
|
|||
vec3 discoColor = vec3( |
|||
0.5 + abs(0.5 - y) * cos(uTime * 10.0), |
|||
0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c / 4.0)), |
|||
0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s / 4.0)))); |
|||
|
|||
vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75 + 0.25); |
|||
objectColor = objectColor * (1.0 - uDisco) + discoColor * uDisco; |
|||
|
|||
float ambientStrength = 0.3; |
|||
vec3 lightColor = vec3(1.0, 1.0, 1.0); |
|||
vec3 lightPos = vec3(uMaxY * 2.0, uMaxY * 2.0, uMaxY * 2.0); |
|||
vec3 ambient = ambientStrength * lightColor; |
|||
protected override unsafe void OnOpenGlInit(GlInterface GL) => _content.Init(GL, GlVersion); |
|||
|
|||
protected override void OnOpenGlDeinit(GlInterface GL) => _content.Deinit(GL); |
|||
|
|||
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); |
|||
|
|||
} |
|||
");
|
|||
|
|||
[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 static void CheckError(GlInterface gl) |
|||
{ |
|||
int err; |
|||
while ((err = gl.GetError()) != GL_NO_ERROR) |
|||
Console.WriteLine(err); |
|||
} |
|||
|
|||
protected override unsafe void OnOpenGlInit(GlInterface GL) |
|||
{ |
|||
CheckError(GL); |
|||
|
|||
Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}"; |
|||
|
|||
// 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); |
|||
CheckError(GL); |
|||
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); |
|||
CheckError(GL); |
|||
fixed (void* pdata = _indices) |
|||
GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata), |
|||
GL_STATIC_DRAW); |
|||
CheckError(GL); |
|||
_vertexArrayObject = GL.GenVertexArray(); |
|||
GL.BindVertexArray(_vertexArrayObject); |
|||
CheckError(GL); |
|||
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) |
|||
{ |
|||
// 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); |
|||
} |
|||
|
|||
static Stopwatch St = Stopwatch.StartNew(); |
|||
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); |
|||
CheckError(GL); |
|||
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"); |
|||
var timeLoc = GL.GetUniformLocationString(_shaderProgram, "uTime"); |
|||
var discoLoc = GL.GetUniformLocationString(_shaderProgram, "uDisco"); |
|||
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.Uniform1f(timeLoc, (float)St.Elapsed.TotalSeconds); |
|||
GL.Uniform1f(discoLoc, _disco); |
|||
CheckError(GL); |
|||
GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero); |
|||
|
|||
CheckError(GL); |
|||
if (_disco > 0.01) |
|||
RequestNextFrameRendering(); |
|||
} |
|||
|
|||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) |
|||
{ |
|||
if (change.Property == YawProperty || change.Property == RollProperty || change.Property == PitchProperty || |
|||
change.Property == DiscoProperty) |
|||
if (_knobs == null) |
|||
return; |
|||
_content.OnOpenGlRender(gl, fb, new PixelSize((int)Bounds.Width, (int)Bounds.Height), |
|||
_knobs.Yaw, _knobs.Pitch, _knobs.Roll, _knobs.Disco); |
|||
if (_knobs.Disco > 0.01) |
|||
RequestNextFrameRendering(); |
|||
base.OnPropertyChanged(change); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,41 +1,11 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.ProgressBarPage"> |
|||
<StackPanel Orientation="Vertical" Spacing="4"> |
|||
<TextBlock Classes="h2">A progress bar control</TextBlock> |
|||
<StackPanel Spacing="5"> |
|||
<StackPanel Orientation="Horizontal" Spacing="5"> |
|||
<TextBlock VerticalAlignment="Center">Maximum</TextBlock> |
|||
<NumericUpDown x:Name="maximum" Value="100" VerticalAlignment="Center"/> |
|||
</StackPanel> |
|||
<StackPanel Orientation="Horizontal" Spacing="5"> |
|||
<TextBlock VerticalAlignment="Center">Minimum</TextBlock> |
|||
<NumericUpDown x:Name="minimum" Value="0" VerticalAlignment="Center"/> |
|||
</StackPanel> |
|||
<StackPanel Orientation="Horizontal" Spacing="5"> |
|||
<TextBlock VerticalAlignment="Center">Progress Text Format</TextBlock> |
|||
<TextBox x:Name="stringFormat" Text="{}{0:0}%" VerticalAlignment="Center"/> |
|||
</StackPanel> |
|||
<CheckBox x:Name="showProgress" Margin="10,16,0,0" Content="Show Progress Text" /> |
|||
<CheckBox x:Name="isIndeterminate" Margin="10,16,0,0" Content="Toggle Indeterminate" /> |
|||
<StackPanel Orientation="Horizontal" Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16"> |
|||
<StackPanel Spacing="16"> |
|||
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}" |
|||
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" ProgressTextFormat="{Binding #stringFormat.Text}"/> |
|||
</StackPanel> |
|||
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical" |
|||
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" ProgressTextFormat="{Binding #stringFormat.Text}"/> |
|||
</StackPanel> |
|||
<StackPanel Margin="16"> |
|||
<Slider Name="hprogress" Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" Value="40" /> |
|||
<Slider Name="vprogress" Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" Value="60" /> |
|||
</StackPanel> |
|||
|
|||
<StackPanel Spacing="10"> |
|||
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" |
|||
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}"/> |
|||
<ProgressBar VerticalAlignment="Center" IsIndeterminate="True" /> |
|||
<ProgressBar VerticalAlignment="Center" Value="5" Maximum="10" /> |
|||
<ProgressBar VerticalAlignment="Center" Value="50" /> |
|||
<ProgressBar VerticalAlignment="Center" Value="50" Minimum="25" Maximum="75" /> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
</UserControl> |
|||
|
|||
Loading…
Reference in new issue