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.
195 lines
8.3 KiB
C#
195 lines
8.3 KiB
C#
using System.Numerics;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.Graphics.Clyde;
|
|
using Robust.Client.Utility;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Graphics.RSI;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Utility;
|
|
using static Robust.Client.GameObjects.SpriteComponent;
|
|
|
|
namespace Robust.Client.GameObjects;
|
|
|
|
// This partial class contains code related to actually rendering sprites.
|
|
public sealed partial class SpriteSystem
|
|
{
|
|
public void RenderSprite(
|
|
Entity<SpriteComponent> sprite,
|
|
DrawingHandleWorld drawingHandle,
|
|
Angle eyeRotation,
|
|
Angle worldRotation,
|
|
Vector2 worldPosition)
|
|
{
|
|
RenderSprite(sprite,
|
|
drawingHandle,
|
|
eyeRotation,
|
|
worldRotation,
|
|
worldPosition,
|
|
sprite.Comp.EnableDirectionOverride ? sprite.Comp.DirectionOverride : null);
|
|
}
|
|
|
|
public void RenderSprite(
|
|
Entity<SpriteComponent> sprite,
|
|
DrawingHandleWorld drawingHandle,
|
|
Angle eyeRotation,
|
|
Angle worldRotation,
|
|
Vector2 worldPosition,
|
|
Direction? overrideDirection)
|
|
{
|
|
// TODO SPRITE RENDERING
|
|
// Add fast path for simple sprites.
|
|
// I.e., when a sprite is modified, check if it is "simple". If it is. cache texture information in a struct
|
|
// and use a fast path here.
|
|
// E.g., simple 1-directional, 1-layer sprites can basically become a direct texture draw call. (most in game items).
|
|
// Similarly, 1-directional multi-layer sprites can become a sequence of direct draw calls (most in game walls).
|
|
|
|
if (!sprite.Comp.IsInert)
|
|
_queuedFrameUpdate.Add(sprite);
|
|
|
|
var angle = worldRotation + eyeRotation; // angle on-screen. Used to decide the direction of 4/8 directional RSIs
|
|
angle = angle.Reduced().FlipPositive(); // Reduce the angles to fix math shenanigans
|
|
|
|
var cardinal = Angle.Zero;
|
|
|
|
// If we have a 1-directional sprite then snap it to try and always face it south if applicable.
|
|
if (sprite.Comp is {NoRotation: false, SnapCardinals: true})
|
|
cardinal = angle.RoundToCardinalAngle();
|
|
|
|
// worldRotation + eyeRotation should be the angle of the entity on-screen. If no-rot is enabled this is just set to zero.
|
|
// However, at some point later the eye-matrix is applied separately, so we subtract -eye rotation for now:
|
|
var entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, sprite.Comp.NoRotation ? -eyeRotation : worldRotation - cardinal);
|
|
var spriteMatrix = Matrix3x2.Multiply(sprite.Comp.LocalMatrix, entityMatrix);
|
|
|
|
// Fast path for when all sprites use the same transform matrix
|
|
if (!sprite.Comp.GranularLayersRendering)
|
|
{
|
|
foreach (var layer in sprite.Comp.Layers)
|
|
{
|
|
RenderLayer(layer, drawingHandle, ref spriteMatrix, angle, overrideDirection);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//Default rendering (NoRotation = false)
|
|
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation);
|
|
var transformDefault = Matrix3x2.Multiply(sprite.Comp.LocalMatrix, entityMatrix);
|
|
|
|
//Snap to cardinals
|
|
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, worldRotation - angle.RoundToCardinalAngle());
|
|
var transformSnap = Matrix3x2.Multiply(sprite.Comp.LocalMatrix, entityMatrix);
|
|
|
|
//No rotation
|
|
entityMatrix = Matrix3Helpers.CreateTransform(worldPosition, -eyeRotation);
|
|
var transformNoRot = Matrix3x2.Multiply(sprite.Comp.LocalMatrix, entityMatrix);
|
|
|
|
foreach (var layer in sprite.Comp.Layers)
|
|
{
|
|
switch (layer.RenderingStrategy)
|
|
{
|
|
case LayerRenderingStrategy.UseSpriteStrategy:
|
|
RenderLayer(layer, drawingHandle, ref spriteMatrix, angle, overrideDirection);
|
|
break;
|
|
case LayerRenderingStrategy.Default:
|
|
RenderLayer(layer, drawingHandle, ref transformDefault, angle, overrideDirection);
|
|
break;
|
|
case LayerRenderingStrategy.NoRotation:
|
|
RenderLayer(layer, drawingHandle, ref transformNoRot, angle, overrideDirection);
|
|
break;
|
|
case LayerRenderingStrategy.SnapToCardinals:
|
|
RenderLayer(layer, drawingHandle, ref transformSnap, angle, overrideDirection);
|
|
break;
|
|
default:
|
|
Log.Error($"Tried to render a layer with unknown rendering stragegy: {layer.RenderingStrategy}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Render a layer. This assumes that the input angle is between 0 and 2pi.
|
|
/// </summary>
|
|
private void RenderLayer(Layer layer, DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatrix, Angle angle, Direction? overrideDirection)
|
|
{
|
|
if (!layer.Visible || layer.Blank)
|
|
return;
|
|
|
|
var state = layer._actualState;
|
|
var dir = state == null ? RsiDirection.South : Layer.GetDirection(state.RsiDirections, angle);
|
|
|
|
// Set the drawing transform for this layer
|
|
layer.GetLayerDrawMatrix(dir, out var layerMatrix, layer.Owner.Comp.NoRotation);
|
|
|
|
// The direction used to draw the sprite can differ from the one that the angle would naively suggest,
|
|
// due to direction overrides or offsets.
|
|
if (overrideDirection != null && state != null)
|
|
dir = overrideDirection.Value.Convert(state.RsiDirections);
|
|
dir = dir.OffsetRsiDir(layer.DirOffset);
|
|
|
|
var texture = state?.GetFrame(dir, layer.AnimationFrame) ?? layer.Texture ?? GetFallbackTexture();
|
|
|
|
// TODO SPRITE
|
|
// Refactor shader-param-layers to a separate layer type after layers are split into types & collections.
|
|
// I.e., separate Layer -> RsiLayer, TextureLayer, LayerCollection, SpriteLayer, and ShaderLayer
|
|
if (layer.CopyToShaderParameters != null)
|
|
{
|
|
HandleShaderLayer(layer, texture, layer.CopyToShaderParameters);
|
|
return;
|
|
}
|
|
|
|
// Set the drawing transform for this layer
|
|
var transformMatrix = Matrix3x2.Multiply(layerMatrix, spriteMatrix);
|
|
drawingHandle.SetTransform(in transformMatrix);
|
|
|
|
if (layer.Shader != null)
|
|
drawingHandle.UseShader(layer.Shader);
|
|
|
|
var layerColor = layer.Owner.Comp.color * layer.Color;
|
|
var textureSize = texture.Size / (float) EyeManager.PixelsPerMeter;
|
|
var quad = Box2.FromDimensions(textureSize / -2, textureSize);
|
|
|
|
if (layer.UnShaded)
|
|
{
|
|
DebugTools.AssertNull(layer.Shader);
|
|
DebugTools.Assert(layerColor is {R: >= 0, G: >= 0, B: >= 0, A: >= 0}, "Default shader should not be used with negative color modulation.");
|
|
|
|
// Negative color modulation values are by the default shader to disable light shading.
|
|
// Specifically we set colour = - 1 - colour
|
|
// This is good enough to ensure that non-negative values become negative & is trivially invertible.
|
|
layerColor = new(new Vector4(-1) - layerColor.RGBA);
|
|
}
|
|
|
|
drawingHandle.DrawTextureRectRegion(texture, quad, layerColor);
|
|
|
|
if (layer.Shader != null)
|
|
drawingHandle.UseShader(null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle a a "fake layer" that just exists to modify the parameters of a shader being used by some other
|
|
/// layer.
|
|
/// </summary>
|
|
private void HandleShaderLayer(Layer layer, Texture texture, CopyToShaderParameters @params)
|
|
{
|
|
// Multiple atrocities to god being committed right here.
|
|
var otherLayerIdx = layer._parent.LayerMap[@params.LayerKey!];
|
|
var otherLayer = layer._parent.Layers[otherLayerIdx];
|
|
if (otherLayer.Shader is not { } shader)
|
|
return;
|
|
|
|
if (!shader.Mutable)
|
|
otherLayer.Shader = shader = shader.Duplicate();
|
|
|
|
var clydeTexture = Clyde.RenderHandle.ExtractTexture(texture, null, out var csr);
|
|
|
|
if (@params.ParameterTexture is { } paramTexture)
|
|
shader.SetParameter(paramTexture, clydeTexture);
|
|
|
|
if (@params.ParameterUV is not { } paramUV)
|
|
return;
|
|
|
|
var sr = Clyde.RenderHandle.WorldTextureBoundsToUV(clydeTexture, csr);
|
|
var uv = new Vector4(sr.Left, sr.Bottom, sr.Right, sr.Top);
|
|
shader.SetParameter(paramUV, uv);
|
|
}
|
|
}
|