215 lines
7.2 KiB
C#
215 lines
7.2 KiB
C#
using ChestPreview.Configs;
|
|
using ChestPreview.Models;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Vintagestory.API.Client;
|
|
using Vintagestory.API.Common;
|
|
using Vintagestory.API.MathTools;
|
|
|
|
namespace ChestPreview.Rendering;
|
|
|
|
internal class WorldBillboardRenderer(ICoreClientAPI api, Config config, CardRenderer cardRenderer) : IRenderer
|
|
{
|
|
private static float card_cell_padding => 2f;
|
|
private static float card_cell_width => 72f;
|
|
private static float card_cell_gap => 4f;
|
|
private static float billboard_brightness => 1.18f;
|
|
|
|
private readonly ICoreClientAPI api = api;
|
|
private readonly List<PreviewTarget> frameTargets = [];
|
|
private readonly CardRenderer cardRenderer = cardRenderer;
|
|
private readonly PreviewTargetProvider targetProvider = new(api, config);
|
|
private readonly WorldBillboardPresenter worldBillboardPresenter = new(api, config);
|
|
private readonly MeshRef quadMeshRef = api.Render.UploadMesh(
|
|
QuadMeshUtil.GetCustomQuadModelData(
|
|
1f,
|
|
v: 1f,
|
|
0f,
|
|
v2: 0f,
|
|
dx: -1f,
|
|
dy: -1f,
|
|
dw: 2f,
|
|
dh: 2f,
|
|
r: byte.MaxValue,
|
|
g: byte.MaxValue,
|
|
b: byte.MaxValue,
|
|
a: byte.MaxValue
|
|
)
|
|
);
|
|
private bool disposed;
|
|
|
|
public ICoreClientAPI Api { get; } = api;
|
|
|
|
public Config Config { get; } = config;
|
|
|
|
public double RenderOrder => 0.61;
|
|
|
|
public int RenderRange => 24;
|
|
|
|
public void OnRenderFrame(float deltaTime, EnumRenderStage stage)
|
|
{
|
|
if (disposed || stage != EnumRenderStage.AfterOIT)
|
|
return;
|
|
|
|
frameTargets.Clear();
|
|
targetProvider.CollectTargets(deltaTime, frameTargets);
|
|
worldBillboardPresenter.PrepareFrame(frameTargets);
|
|
|
|
if (worldBillboardPresenter.FrameBillboards.Count == 0)
|
|
return;
|
|
|
|
api.Render.GlDisableCullFace();
|
|
api.Render.GLEnableDepthTest();
|
|
api.Render.GlToggleBlend(true, EnumBlendMode.PremultipliedAlpha);
|
|
try
|
|
{
|
|
foreach (BillboardTarget billboardTarget in worldBillboardPresenter.FrameBillboards)
|
|
{
|
|
int maxColumns = GetBillboardMaxColumns(billboardTarget);
|
|
if (!cardRenderer.TryGetOrCreateCardTexture(billboardTarget.PreviewTarget, maxColumns, out LoadedTexture texture))
|
|
continue;
|
|
|
|
RenderBillboard(billboardTarget, texture, maxColumns);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
StopShader(api.Render.CurrentActiveShader);
|
|
api.Render.GlToggleBlend(false, EnumBlendMode.PremultipliedAlpha);
|
|
api.Render.GLDisableDepthTest();
|
|
api.Render.GlEnableCullFace();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (disposed)
|
|
return;
|
|
|
|
disposed = true;
|
|
frameTargets.Clear();
|
|
worldBillboardPresenter.Clear();
|
|
targetProvider.Dispose();
|
|
quadMeshRef.Dispose();
|
|
}
|
|
|
|
private void RenderBillboard(BillboardTarget billboardTarget, LoadedTexture texture, int maxColumns)
|
|
{
|
|
EntityPlayer? playerEntity = api.World.Player?.Entity;
|
|
if (playerEntity?.CameraPos == null)
|
|
return;
|
|
|
|
float referenceWidthPx = GetCardWidthPx(maxColumns);
|
|
float widthScale = texture.Width / Math.Max(1f, referenceWidthPx);
|
|
float textureAspect = texture.Height / (float)System.Math.Max(1, texture.Width);
|
|
float scaledWidth;
|
|
float textureHeight;
|
|
Vec3d center;
|
|
|
|
if (billboardTarget.IsFrontPlacement)
|
|
{
|
|
scaledWidth = billboardTarget.Width * widthScale;
|
|
textureHeight = scaledWidth * textureAspect;
|
|
|
|
if (scaledWidth > billboardTarget.Width)
|
|
{
|
|
scaledWidth = billboardTarget.Width;
|
|
textureHeight = scaledWidth * textureAspect;
|
|
}
|
|
|
|
if (textureHeight > billboardTarget.Height)
|
|
{
|
|
textureHeight = billboardTarget.Height;
|
|
scaledWidth = textureHeight / Math.Max(0.0001f, textureAspect);
|
|
}
|
|
|
|
center = billboardTarget.Center;
|
|
}
|
|
else
|
|
{
|
|
scaledWidth = billboardTarget.Width * widthScale;
|
|
textureHeight = scaledWidth * textureAspect;
|
|
center = new Vec3d(
|
|
billboardTarget.PreviewTarget.Anchor.X,
|
|
billboardTarget.PreviewTarget.Anchor.Y + textureHeight / 2f,
|
|
billboardTarget.PreviewTarget.Anchor.Z
|
|
);
|
|
}
|
|
|
|
float[] modelMatrix = CreateModelMatrix(billboardTarget, center, scaledWidth, textureHeight, playerEntity.CameraPos);
|
|
float brightness = billboard_brightness;
|
|
IStandardShaderProgram shader = api.Render.PreparedStandardShader(
|
|
billboardTarget.PreviewTarget.BlockEntity.Pos.X,
|
|
billboardTarget.PreviewTarget.BlockEntity.Pos.Y,
|
|
billboardTarget.PreviewTarget.BlockEntity.Pos.Z,
|
|
new Vec4f(brightness, brightness, brightness, 1f)
|
|
);
|
|
|
|
shader.Tex2D = texture.TextureId;
|
|
shader.AlphaTest = 0.01f;
|
|
shader.RgbaAmbientIn = new Vec3f(brightness, brightness, brightness);
|
|
shader.RgbaLightIn = new Vec4f(brightness, brightness, brightness, 1f);
|
|
shader.RgbaGlowIn = new Vec4f(0f, 0f, 0f, 0f);
|
|
shader.RgbaTint = new Vec4f(brightness, brightness, brightness, 1f);
|
|
shader.ModelMatrix = modelMatrix;
|
|
shader.ViewMatrix = api.Render.CameraMatrixOriginf;
|
|
shader.ProjectionMatrix = api.Render.CurrentProjectionMatrix;
|
|
|
|
api.Render.BindTexture2d(texture.TextureId);
|
|
api.Render.RenderMesh(quadMeshRef);
|
|
}
|
|
|
|
private static void StopShader(IShaderProgram? shader)
|
|
{
|
|
if (shader == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
shader.Stop();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private static float GetCardWidthPx(int columns)
|
|
{
|
|
int safeColumns = Math.Max(1, columns);
|
|
return card_cell_padding * 2f + safeColumns * card_cell_width + (safeColumns - 1) * card_cell_gap;
|
|
}
|
|
|
|
private int GetBillboardMaxColumns(BillboardTarget billboardTarget)
|
|
{
|
|
int columnsPerBlock = Math.Max(1, Config.BillboardColumnsPerBlock <= 0 ? 1 : Config.BillboardColumnsPerBlock);
|
|
int blockWidth = Math.Max(1, (int)MathF.Round(billboardTarget.Width));
|
|
return columnsPerBlock * blockWidth;
|
|
}
|
|
|
|
private static float[] CreateModelMatrix(BillboardTarget billboardTarget, Vec3d center, float width, float height, Vec3d cameraPos)
|
|
{
|
|
float halfWidth = width / 2f;
|
|
float halfHeight = height / 2f;
|
|
|
|
return
|
|
[
|
|
billboardTarget.Right.X * halfWidth,
|
|
billboardTarget.Right.Y * halfWidth,
|
|
billboardTarget.Right.Z * halfWidth,
|
|
0f,
|
|
billboardTarget.Up.X * halfHeight,
|
|
billboardTarget.Up.Y * halfHeight,
|
|
billboardTarget.Up.Z * halfHeight,
|
|
0f,
|
|
billboardTarget.Forward.X,
|
|
billboardTarget.Forward.Y,
|
|
billboardTarget.Forward.Z,
|
|
0f,
|
|
(float)(center.X - cameraPos.X),
|
|
(float)(center.Y - cameraPos.Y),
|
|
(float)(center.Z - cameraPos.Z),
|
|
1f
|
|
];
|
|
}
|
|
}
|