Untitled

 avatar
unknown
plain_text
2 years ago
6.6 kB
6
Indexable
/// <summary>
/// Class to project points from a position onto an lcd
/// NOTE: Only works if the ViewPoint is infront of the lcd -> Transparent LCDS from the back dont work
/// </summary>
public class TextPanelRenderingContext
{
    DebugAPI Draw;
    public Vector3D ViewPoint { get; private set; }
    public IMyTextPanel TextPanel { get; private set; }
    public Vector2 PixelMultiplier { get; private set; }

    private readonly Vector3D Normal = Vector3D.Backward;
    public static readonly double TextPanelThickness = 0.03f;
    public static readonly float TextPanelTextureMargin = 0.335f;
    private static readonly Vector3D ProjectionOffset = Vector3D.Backward * ((2.5d / 2d) - TextPanelThickness);

    /// <summary>
    /// Initializes the renderer to a working state
    /// </summary>
    /// <param name="lcd">The lcd you want to project to</param>
    /// <param name="viewPointDirection">Direction to view from local to lcd's matrix</param>
    public TextPanelRenderingContext(ref IMyTextPanel lcd, Vector3D viewPointDirection, DebugAPI debugAPI = null, Program p = null)
    {
        Draw = debugAPI;
        TextPanel = lcd;
        ViewPoint = viewPointDirection + ProjectionOffset;

        var screenSize = GetTextPanelSizeFromGridView(TextPanel);
        PixelMultiplier = TextPanel.TextureSize / ((Vector2)screenSize * (2.5f - TextPanelTextureMargin));
        p.Echo(screenSize.ToString());
        p.Echo(PixelMultiplier.ToString());
    }

    private static Vector2I GetTextPanelSizeFromGridView(IMyTextPanel textPanel)
    {
        Vector3I lcdSize = textPanel.Max - textPanel.Min;
        Vector2I screenSize = new Vector2I();
        switch (textPanel.Orientation.Forward)
        {
            case Base6Directions.Direction.Forward:
                screenSize = new Vector2I(lcdSize.X, lcdSize.Y);
                break;
            case Base6Directions.Direction.Backward:
                screenSize = new Vector2I(lcdSize.X, lcdSize.Y);
                break;
            case Base6Directions.Direction.Left:
                screenSize = new Vector2I(lcdSize.Z, lcdSize.Y);
                break;
            case Base6Directions.Direction.Right:
                screenSize = new Vector2I(lcdSize.Z, lcdSize.Y);
                break;
            case Base6Directions.Direction.Up:
                screenSize = new Vector2I(lcdSize.X, lcdSize.Z);
                break;
            case Base6Directions.Direction.Down:
                screenSize = new Vector2I(lcdSize.X, lcdSize.Z);
                break;
            default:
                throw new ArgumentException("Unknown orientation");
        }
        screenSize += new Vector2I(1, 1);
        return screenSize;
    }

    /// <summary>
    /// Projects the given point onto LCD screen coordinates given in pixels
    /// </summary>
    /// <param name="worldPoint">The point to project</param>
    /// <returns>Screen coordinate in pixels or null if projection is not on lcd</returns>
    public Vector2? ProjectPoint(Vector3D worldPoint)
    {
        Draw.DrawLine(worldPoint, worldPoint + LocalDirToWorldDir(ProjectionOffset, TextPanel.WorldMatrix), Color.Green, 0.02f, 10);
        // direction from the viewPoint to the worldPoint
        Vector3D referenceWorldPosition = TextPanel.WorldMatrix.Translation; // block.WorldMatrix.Translation is the same as block.GetPosition() btw
        // Convert worldPosition into a world direction
        Vector3D worldDirection = worldPoint - referenceWorldPosition; // This is a vector starting at the reference block pointing at your desired position
        // Convert worldDirection into a local direction
        Vector3D localPointToProject = Vector3D.TransformNormal(worldDirection, MatrixD.Transpose(TextPanel.WorldMatrix)) + ProjectionOffset; // Note that we transpose to go from world -> body
        // ray direction in local space
        Vector3D localRayDirection = localPointToProject - ViewPoint;
        //// we dont normalize to keep it at max performance
        //localRayDirection.Normalize();
        Draw.DrawLine(LocalPosToWorldPos(ViewPoint, TextPanel.WorldMatrix), LocalPosToWorldPos(ViewPoint, TextPanel.WorldMatrix) + LocalDirToWorldDir(localRayDirection, TextPanel.WorldMatrix), Color.LightCoral, 0.01f, 10);
        // project the plane onto the plane
        
        Vector2? projectedLocalPoint = PlaneIntersection(ViewPoint, localRayDirection);
        if (projectedLocalPoint != null)
        {
            var projectedLocalPointNonNullable = (Vector2)projectedLocalPoint;
            Draw.DrawPoint(LocalPosToWorldPos(new Vector3D(projectedLocalPointNonNullable.X, projectedLocalPointNonNullable.Y, -2.5 / 2d), TextPanel.WorldMatrix), Color.Red, 0.01f, 10);
            // convert it to pixels
            Vector2 projectedLocalPointPixels = projectedLocalPointNonNullable * PixelMultiplier * new Vector2(1, -1);
            projectedLocalPointPixels += TextPanel.TextureSize / 2f;
            if (projectedLocalPointPixels.X >= 0 && projectedLocalPointPixels.Y >= 0 && projectedLocalPointPixels.X < TextPanel.SurfaceSize.X && projectedLocalPointPixels.Y < TextPanel.SurfaceSize.Y)
            {
                return projectedLocalPointPixels;
            }
        }
        return null;
    }

    /// <summary>
    /// Calculates the intersection point from the given line and a plane with origin (0,0,0) and the normal (static)
    /// </summary>
    /// <param name="origin">Line origin</param>
    /// <param name="dir">Line direction</param>
    /// <returns>The projected point</returns>
    private Vector2? PlaneIntersection(Vector3D origin, Vector3D dir)
    {
        if (dir.Z >= 0)
        {
            return null;
        }
        var t = -DotNormal(origin) / DotNormal(dir);
        Vector3D res = origin + t * dir;
        return new Vector2((float)res.X, (float)res.Y);
    }

    /// <summary>
    /// Calculates the dot-product of a specified Vector3D and the normal vector (static)
    /// </summary>
    /// <param name="value">The Vector3D to calculate the dot product of</param>
    private double DotNormal(Vector3D value)
    {
        return (double)(Normal.X * value.X + Normal.Y * value.Y + Normal.Z * value.Z);
    }

    Vector3D LocalDirToWorldDir(Vector3D dir, MatrixD matrix)
    {
        return Vector3D.TransformNormal(dir, matrix);
    }

    Vector3D LocalPosToWorldPos(Vector3D pos, MatrixD matrix)
    {
        return Vector3D.Transform(pos, matrix);
    }

}
Editor is loading...