core/player/PlayerPacCamera.cs
using Sandbox.Rendering;
using Sandbox.Utility;
public class PlayerPacCamera : Component {
[Property, ReadOnly] public BasePlayer Owner {get; set;}
[Property, ReadOnly] public Vector2Int RenderSize {get; set;} = new(640,480);
[Property, ReadOnly] public CameraComponent RenderCamera {get; set;}
[Property, ReadOnly] public CameraComponent DrawCamera {get; set;}
[Property] public bool AlwaysRerender {get; set;}
[Property, Change(nameof(UpdateVideoMask))] public bool FullScreenFMV {get; set;}
[Property, Range(-1,1)] public float Fade {get; set;}
public enum TransitionType {
None,
SlideLeft,
SlideRight,
Fade,
}
public TransitionType DoTransition {get; set;}
private TransitionType CurrentTransition {get; set;}
public TimeUntil TransitionTime {get; set;}
[Property, ReadOnly] public Texture StaticCurrent {get; set;}
[Property, ReadOnly] public Texture StaticLast {get; set;}
[Property, ReadOnly] public Texture VideoMask {get; set;}
[Property, ReadOnly] public Texture VideoCurrent {get; set;}
[Property, ReadOnly] public Texture VideoLast {get; set;}
[Property, ReadOnly] public Texture Composite {get; set;}
[Property, ReadOnly] public Action DelayMoveAction {get; set;}
[Property, ReadOnly, Range(-1,1)] public int Turn {get; set;}
[Property] public Texture OverlayTexture {get; set;}
[Property, Range(0,1)] public float OverlayStrength {get; set;}
protected override void OnAwake() {
Composite = Texture.CreateRenderTarget().WithUAVBinding().WithSize(RenderSize).Create();
Spawn.SetOwner(this);
Spawn.Component(nameof(RenderCamera));
RenderCamera.BackgroundColor = Color.Black;
Spawn.Component(nameof(DrawCamera));
DrawCamera.RenderTags.Add("null");
DrawCamera.BackgroundColor = Color.Black;
DrawCamera.Priority = 1000;
DrawCamera.ClearCommandLists();
var cmds = new CommandList();
cmds.Attributes.Set("Frame", Composite);
cmds.Blit(Material.FromShader(Shader.Load("shaders/post_draw.shader")));
DrawCamera.AddCommandList(cmds, Stage.AfterPostProcess);
base.OnAwake();
}
public Vector2 PointToRenderPixels(Vector3 point) {
RenderCamera.CustomSize = RenderSize;
var uv = RenderCamera.PointToScreenNormal(point);
if (uv.x == 0) uv.x = -1; else if (uv.x == 1) uv.x = 2;
if (uv.y == 0) uv.y = -1; else if (uv.y == 1) uv.y = 2;
uv -= 0.5f;
uv.y *= ((float)RenderSize.x/RenderSize.y) / Screen.Aspect;
uv += 0.5f;
uv *= RenderSize;
uv.x = uv.x.Floor();
uv.y = uv.y.Floor();
return uv;
}
public void Update() {
LocalTransform = global::Transform.Zero;
Mouse.Visibility = MouseVisibility.Visible;
Mouse.CursorType = "iso2_pointer";
if (Turn == -1)
Mouse.CursorType = "iso2_left";
if (Turn == 1)
Mouse.CursorType = "iso2_right";
if (FullScreenFMV)
Mouse.CursorType = "None";
if (TransitionTime)
CurrentTransition = TransitionType.None;
else foreach (var animator in Scene.GetAllComponents<NpcAnimator>())
animator.ResetToIdle();
TerrainSuperBlendController.Set(Scene, Scene.RenderAttributes);
RenderStatic();
RenderVideo();
Scene.RenderAttributes.Clear();
DoComposite();
}
private int ViewHash = 0;
private void RenderStatic() {
var newtransition = DoTransition;
DoTransition = TransitionType.None;
if (FullScreenFMV)
return;
var angle = HashCode.Combine(WorldTransform, RenderCamera.FieldOfView);
if (angle == ViewHash && !AlwaysRerender)
return;
ViewHash = angle;
if (!AlwaysRerender) {
foreach (var animator in Scene.GetAllComponents<NpcAnimator>())
animator.ResetToIdle();
}
StaticLast?.Dispose();
StaticLast = StaticCurrent;
StaticCurrent = Texture.CreateRenderTarget().WithSize(RenderSize).Create();
//RenderCamera.RenderExcludeTags.Add("dynamic");
RenderCamera.RenderExcludeTags.Add("iso_waypoint");
RenderCamera.RenderExcludeTags.Add("iso_entity");
RenderCamera.RenderToTexture(StaticCurrent);
RenderCamera.RenderExcludeTags.RemoveAll();
UpdateVideoMask();
LastVideoRender = 20f;
if (newtransition != TransitionType.None) {
CurrentTransition = newtransition;
TransitionTime = 0.3f;
}
}
public void InvalidateView() {
ViewHash = 0;
}
public void UpdateVideoMask() {
VideoMask?.Dispose();
if (FullScreenFMV) {
VideoMask = Texture.Create(1, 1).WithData(new byte[4] {255, 255, 255, 255}).Finish();
return;
}
var tex = new Bitmap(RenderSize.x, RenderSize.y);
tex.Clear(Color.Black);
tex.SetFill(Color.White);
foreach (var bound in Scene.GetAllComponents<FmvBoundsCollection>()) {
if (!bound.Test(RenderCamera.GetFrustum(new(0,Screen.Size))))
continue;
var created = false;
Rect rough = new();
foreach (var corner in bound.GetCorners()) {
var point = PointToRenderPixels(bound.WorldTransform.PointToWorld(corner));
//tex.DrawCircle(point, 2);
//DebugOverlay.Sphere(new(bound.WorldTransform.PointToWorld(corner), 1), Color.Red, 20f, default, true);
if (!created)
rough = new(point);
else
rough = rough.AddPoint(point);
created = true;
}
if (rough.Width.AlmostEqual(0f) || rough.Height.AlmostEqual(0f))
continue;
tex.DrawRect(rough);
}
VideoMask = tex.ToTexture();
}
private TimeSince LastVideoRender;
private void RenderVideo() {
if (CurrentTransition != TransitionType.None)
return;
if (LastVideoRender < 1f/(FullScreenFMV ? 30f : 15f))
return;
LastVideoRender = 0f;
VideoLast?.Dispose();
VideoLast = VideoCurrent;
VideoCurrent = Texture.CreateRenderTarget().WithUAVBinding().WithSize(RenderSize).Create();
var videonew = Texture.CreateRenderTarget().WithUAVBinding().WithSize(RenderSize).Create();
RenderCamera.RenderExcludeTags.Add("iso_waypoint");
RenderCamera.RenderExcludeTags.Add("iso_entity");
RenderCamera.RenderExcludeTags.Add("flame");
RenderCamera.RenderToTexture(videonew);
RenderCamera.RenderExcludeTags.RemoveAll();
var shader = new ComputeShader("shaders/cs_pac_video_p_frames.shader");
shader.Attributes.Set("VideoCurrent", videonew);
shader.Attributes.Set("VideoLast", VideoLast);
shader.Attributes.Set("RenderSize", VideoCurrent.Size);
shader.Attributes.Set("Result", VideoCurrent);
shader.Dispatch(VideoCurrent.Width, VideoCurrent.Height, 1);
videonew.Dispose();
}
private void DoComposite() {
var shader = new ComputeShader("shaders/cs_pac_composite.shader");
shader.Attributes.Set("StaticLast", StaticLast);
shader.Attributes.Set("StaticCurrent", StaticCurrent);
shader.Attributes.Set("VideoMask", VideoMask);
shader.Attributes.Set("VideoCurrent", VideoCurrent);
if (CurrentTransition != TransitionType.None) {
shader.Attributes.Set("RenderVideo", false);
shader.Attributes.Set("TransitionFade", 1f);
if (CurrentTransition == TransitionType.SlideLeft)
shader.Attributes.Set("TransitionSlide",-Easing.SineEaseInOut(TransitionTime.Fraction));
else if (CurrentTransition == TransitionType.SlideRight)
shader.Attributes.Set("TransitionSlide", Easing.SineEaseInOut(TransitionTime.Fraction));
else if (CurrentTransition == TransitionType.Fade)
shader.Attributes.Set("TransitionFade", 1f - TransitionTime.Fraction);
} else {
shader.Attributes.Set("RenderVideo", true);
shader.Attributes.Set("TransitionSlide", 0f);
shader.Attributes.Set("TransitionFade", 0f);
}
shader.Attributes.Set("Overlay", OverlayTexture);
shader.Attributes.Set("OverlayStrength", OverlayStrength);
shader.Attributes.Set("Fade", Fade);
shader.Attributes.Set("RenderSize", Composite.Size);
shader.Attributes.Set("Result", Composite);
shader.Dispatch(Composite.Width, Composite.Height, 1);
}
}