mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Vector3, Vector4, Matrix4, and Quaternion are now gone. Use System.Numerics instead. This commit is just replacing usages, cleaning up using declarations, and moving over the (couple) helpers that are actually important.
626 lines
21 KiB
C#
626 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using System.Runtime.CompilerServices;
|
|
using OpenToolkit.Graphics.OpenGL4;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Client.Graphics.Clyde
|
|
{
|
|
internal partial class Clyde
|
|
{
|
|
/// <summary>
|
|
/// This is an utility class. It does not check whether the OpenGL state machine is set up correctly.
|
|
/// You've been warned:
|
|
/// using things like <see cref="SetUniformTexture" /> if this buffer isn't bound WILL mess things up!
|
|
/// </summary>
|
|
private sealed class GLShaderProgram
|
|
{
|
|
private readonly sbyte?[] _uniformIntCache = new sbyte?[UniCount];
|
|
private readonly Dictionary<string, int> _uniformCache = new();
|
|
public uint Handle = 0;
|
|
private GLShader? _fragmentShader;
|
|
private GLShader? _vertexShader;
|
|
public string? Name { get; }
|
|
private readonly Clyde _clyde;
|
|
|
|
public GLShaderProgram(Clyde clyde, string? name = null)
|
|
{
|
|
_clyde = clyde;
|
|
Name = name;
|
|
}
|
|
|
|
public void Add(GLShader shader)
|
|
{
|
|
ClearCaches();
|
|
switch (shader.Type)
|
|
{
|
|
case ShaderType.VertexShader:
|
|
_vertexShader = shader;
|
|
break;
|
|
case ShaderType.FragmentShader:
|
|
_fragmentShader = shader;
|
|
break;
|
|
default:
|
|
throw new NotImplementedException("Tried to add unsupported shader type!");
|
|
}
|
|
}
|
|
|
|
public void Link((string, uint)[] attribLocations)
|
|
{
|
|
ClearCaches();
|
|
Handle = (uint) GL.CreateProgram();
|
|
_clyde.CheckGlError();
|
|
if (Name != null)
|
|
{
|
|
_clyde.ObjectLabelMaybe(ObjectLabelIdentifier.Program, Handle, Name);
|
|
}
|
|
|
|
if (_vertexShader != null)
|
|
{
|
|
GL.AttachShader(Handle, _vertexShader.ObjectHandle);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
if (_fragmentShader != null)
|
|
{
|
|
GL.AttachShader(Handle, _fragmentShader.ObjectHandle);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
foreach (var (varName, loc) in attribLocations)
|
|
{
|
|
// OpenGL 3.1 is ass and doesn't allow you to specify layout(location = X) in shaders.
|
|
// So we have to manually do it here.
|
|
// Ugh.
|
|
|
|
GL.BindAttribLocation(Handle, loc, varName);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
GL.LinkProgram(Handle);
|
|
_clyde.CheckGlError();
|
|
|
|
GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out var compiled);
|
|
_clyde.CheckGlError();
|
|
if (compiled != 1)
|
|
{
|
|
throw new ShaderCompilationException(GL.GetProgramInfoLog((int) Handle));
|
|
}
|
|
}
|
|
|
|
public void Use()
|
|
{
|
|
if (_clyde._currentProgram == this)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ForceUse();
|
|
}
|
|
|
|
public void ForceUse()
|
|
{
|
|
DebugTools.Assert(Handle != 0);
|
|
|
|
_clyde._currentProgram = this;
|
|
GL.UseProgram(Handle);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void Delete()
|
|
{
|
|
if (Handle == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GL.DeleteProgram(Handle);
|
|
_clyde.CheckGlError();
|
|
Handle = 0;
|
|
}
|
|
|
|
public int GetUniform(string name)
|
|
{
|
|
if (!TryGetUniform(name, out var result))
|
|
{
|
|
ThrowCouldNotGetUniform(name);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public int GetUniform(int id)
|
|
{
|
|
if (!TryGetUniform(id, out var result))
|
|
{
|
|
ThrowCouldNotGetUniform($"[id {id}]");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public bool TryGetUniform(string name, out int index)
|
|
{
|
|
DebugTools.Assert(Handle != 0);
|
|
|
|
if (_uniformCache.TryGetValue(name, out index))
|
|
{
|
|
return index != -1;
|
|
}
|
|
|
|
index = GL.GetUniformLocation(Handle, name);
|
|
_clyde.CheckGlError();
|
|
_uniformCache.Add(name, index);
|
|
return index != -1;
|
|
}
|
|
|
|
public bool TryGetUniform(int id, out int index)
|
|
{
|
|
DebugTools.Assert(Handle != 0);
|
|
DebugTools.Assert(id < UniCount);
|
|
|
|
var value = _uniformIntCache[id];
|
|
if (value.HasValue)
|
|
{
|
|
index = value.Value;
|
|
return index != -1;
|
|
}
|
|
|
|
return InitIntUniform(id, out index);
|
|
}
|
|
|
|
private bool InitIntUniform(int id, out int index)
|
|
{
|
|
string name;
|
|
switch (id)
|
|
{
|
|
case UniIModUV:
|
|
name = UniModUV;
|
|
break;
|
|
case UniILightTexture:
|
|
name = UniLightTexture;
|
|
break;
|
|
case UniIMainTexture:
|
|
name = UniMainTexture;
|
|
break;
|
|
case UniIModelMatrix:
|
|
name = UniModelMatrix;
|
|
break;
|
|
case UniITexturePixelSize:
|
|
name = UniTexturePixelSize;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
index = GL.GetUniformLocation(Handle, name);
|
|
_clyde.CheckGlError();
|
|
_uniformIntCache[id] = (sbyte)index;
|
|
return index != -1;
|
|
}
|
|
|
|
public bool HasUniform(string name) => TryGetUniform(name, out _);
|
|
public bool HasUniform(int id) => TryGetUniform(id, out _);
|
|
|
|
public void BindBlock(string blockName, uint blockBinding)
|
|
{
|
|
var index = (uint) GL.GetUniformBlockIndex(Handle, blockName);
|
|
_clyde.CheckGlError();
|
|
GL.UniformBlockBinding(Handle, index, blockBinding);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void SetUniform(string uniformName, int integer)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
GL.Uniform1(uniformId, integer);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void SetUniform(string uniformName, float single)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
GL.Uniform1(uniformId, single);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void SetUniform(int uniformName, float single)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
GL.Uniform1(uniformId, single);
|
|
}
|
|
|
|
public void SetUniform(string uniformName, float[] singles)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
GL.Uniform1(uniformId, singles.Length, singles);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void SetUniform(int uniformName, float[] singles)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
GL.Uniform1(uniformId, singles.Length, singles);
|
|
}
|
|
|
|
public void SetUniform(string uniformName, in Matrix3x2 matrix)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, matrix);
|
|
}
|
|
|
|
public void SetUniform(int uniformName, in Matrix3x2 matrix)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, matrix);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private unsafe void SetUniformDirect(int slot, in Matrix3x2 value)
|
|
{
|
|
// We put the rows of the input matrix into the columns of our GPU matrices
|
|
// this transpose is required, as in C#, we premultiply vectors with matrices
|
|
// (vM) while GL postmultiplies vectors with matrices (Mv); however, since
|
|
// the Matrix3x2 data is stored row-major, and GL uses column-major, the
|
|
// memory layout is the same (or would be, if Matrix3x2 didn't have an
|
|
// implicit column)
|
|
var buf = stackalloc float[9]{
|
|
value.M11, value.M12, 0,
|
|
value.M21, value.M22, 0,
|
|
value.M31, value.M32, 1
|
|
};
|
|
GL.UniformMatrix3(slot, 1, false, (float*)buf);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void SetUniform(string uniformName, in Matrix4x4 matrix, bool transpose=true)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, matrix, transpose);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private unsafe void SetUniformDirect(int uniformId, in Matrix4x4 value, bool transpose=true)
|
|
{
|
|
Matrix4x4 tmpTranspose = value;
|
|
if (transpose)
|
|
{
|
|
// transposition not supported on GLES2, & no access to _hasGLES
|
|
tmpTranspose = Matrix4x4.Transpose(value);
|
|
}
|
|
GL.UniformMatrix4(uniformId, 1, false, (float*) &tmpTranspose);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void SetUniform(string uniformName, in Vector4 vector)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, vector);
|
|
}
|
|
|
|
public void SetUniform(int uniformName, in Vector4 vector)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, vector);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetUniformDirect(int slot, in Vector4 vector)
|
|
{
|
|
unsafe
|
|
{
|
|
fixed (Vector4* ptr = &vector)
|
|
{
|
|
GL.Uniform4(slot, 1, (float*)ptr);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetUniform(string uniformName, in Color color, bool convertToLinear = true)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, color, convertToLinear);
|
|
}
|
|
|
|
public void SetUniform(int uniformName, in Color color, bool convertToLinear = true)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, color, convertToLinear);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetUniformDirect(int slot, in Color color, bool convertToLinear = true)
|
|
{
|
|
var converted = color;
|
|
if (convertToLinear)
|
|
{
|
|
converted = Color.FromSrgb(color);
|
|
}
|
|
|
|
unsafe
|
|
{
|
|
GL.Uniform4(slot, 1, (float*) &converted);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
|
|
public void SetUniform(string uniformName, Color[] colors)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, colors);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetUniformDirect(int slot, Color[] colors, bool convertToLinear = true)
|
|
{
|
|
scoped Span<Color> colorsToPass;
|
|
if (convertToLinear)
|
|
{
|
|
colorsToPass = stackalloc Color[colors.Length];
|
|
for (int i = 0; i < colors.Length; i++)
|
|
{
|
|
colorsToPass[i] = Color.FromSrgb(colors[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
colorsToPass = colors;
|
|
}
|
|
|
|
unsafe
|
|
{
|
|
fixed (Color* ptr = &colorsToPass[0])
|
|
{
|
|
GL.Uniform4(slot, colorsToPass.Length, (float*)ptr);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetUniform(string uniformName, in Vector3 vector)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, vector);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetUniformDirect(int slot, in Vector3 vector)
|
|
{
|
|
unsafe
|
|
{
|
|
fixed (Vector3* ptr = &vector)
|
|
{
|
|
GL.Uniform3(slot, 1, (float*)ptr);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetUniform(string uniformName, in Vector2 vector)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, vector);
|
|
}
|
|
|
|
public void SetUniform(int uniformName, in Vector2 vector)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, vector);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetUniformDirect(int slot, in Vector2 vector)
|
|
{
|
|
unsafe
|
|
{
|
|
fixed (Vector2* ptr = &vector)
|
|
{
|
|
GL.Uniform2(slot, 1, (float*)ptr);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetUniform(string uniformName, Vector2[] vector)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, vector);
|
|
}
|
|
|
|
public void SetUniform(int uniformName, Vector2[] vector)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, vector);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetUniformDirect(int slot, Vector2[] vectors)
|
|
{
|
|
unsafe
|
|
{
|
|
fixed (Vector2* ptr = &vectors[0])
|
|
{
|
|
GL.Uniform2(slot, vectors.Length, (float*)ptr);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetUniform(string uniformName, bool[] bools)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, bools);
|
|
}
|
|
|
|
public void SetUniform(int uniformName, bool[] bools)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformDirect(uniformId, bools);
|
|
}
|
|
|
|
private void SetUniformDirect(int slot, bool[] bools)
|
|
{
|
|
Span<int> intBools = stackalloc int[bools.Length];
|
|
|
|
for (var i = 0; i < bools.Length; i++)
|
|
{
|
|
intBools[i] = bools[i] ? 1 : 0;
|
|
}
|
|
|
|
unsafe
|
|
{
|
|
fixed (int* intBoolsPtr = intBools)
|
|
{
|
|
GL.Uniform1(slot, bools.Length, intBoolsPtr);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetUniformTexture(string uniformName, TextureUnit textureUnit)
|
|
{
|
|
var uniformId = GetUniform(uniformName);
|
|
SetUniformTextureDirect(uniformId, textureUnit);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetUniformTextureDirect(int slot, TextureUnit value)
|
|
{
|
|
GL.Uniform1(slot, value - TextureUnit.Texture0);
|
|
_clyde.CheckGlError();
|
|
}
|
|
|
|
public void SetUniformTextureMaybe(string uniformName, TextureUnit value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformTextureDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformTextureMaybe(int uniformName, TextureUnit value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformTextureDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, in Vector4 value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(int uniformName, in Vector4 value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, in Color value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(int uniformName, in Color value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, in Matrix3x2 value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, in Matrix4x4 value, bool transpose=true)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value, transpose);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(int uniformName, in Matrix3x2 value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, in Vector2 value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, in Vector2i value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(int uniformName, in Vector2 value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
SetUniformDirect(slot, value);
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, int value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
GL.Uniform1(slot, value);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
|
|
public void SetUniformMaybe(string uniformName, float value)
|
|
{
|
|
if (TryGetUniform(uniformName, out var slot))
|
|
{
|
|
GL.Uniform1(slot, value);
|
|
_clyde.CheckGlError();
|
|
}
|
|
}
|
|
|
|
private void ClearCaches()
|
|
{
|
|
_uniformCache.Clear();
|
|
for (var i = 0; i < UniCount; i++)
|
|
{
|
|
_uniformIntCache[i] = null;
|
|
}
|
|
}
|
|
|
|
private static void ThrowCouldNotGetUniform(string name)
|
|
{
|
|
throw new ArgumentException($"Could not get uniform \"{name}\"!");
|
|
}
|
|
}
|
|
}
|
|
}
|