🌶️ Draw infinite world axes

With the following class derived from Design you can draw the infinite world axes as shown in the following image: 

The axes are drawn in 3 overridden methods:

DrawViewportBackground: the part of the axes beyond the far plane

DrawViewport: the part between the camera planes

DrawOverlay: the part nearer than the camera near plane

[Note] The code supports multiple Viewports since Eyeshot 9 >= 9.0.490 or Eyeshot 10 >= 10.0.859

internal class MyDesign : Design
{

    public Color AxisXColor { get; set; } = Color.Red;
    public Color AxisYColor { get; set; } = Color.Green;
    public Color AxisZColor { get; set; } = Color.Blue;

    // Intersections with the camera planes
    Point3D ptXNear, ptXFar, ptYNear, ptYFar, ptZNear, ptZFar;
    
    protected override void DrawViewportBackground(DrawSceneParams data)
    {
        base.DrawViewportBackground(data);

        ComputeNearFarIntersections(data.Viewport);

        var rc = data.RenderContext;

        rc.PushDepthStencilState();

        rc.SetState(depthStencilStateType.DepthTestOff);
        rc.SetShader(shaderType.NoLights);

        // Draw in 2D the parts of the lines beyond the far camera plane

        // X axis 
        DrawLinesBeyondFar(data.Viewport, ptXNear, ptXFar, AxisXColor);
        // Y axis 
        DrawLinesBeyondFar(data.Viewport, ptYNear, ptYFar, AxisYColor);
        // Z axis 
        DrawLinesBeyondFar(data.Viewport, ptZNear, ptZFar, AxisZColor);

        rc.PopDepthStencilState();
    }

    private void ComputeNearFarIntersections(IViewport viewport)
    {
        Plane farPlane = viewport.Camera.FarPlane;
        Plane nearPlane = viewport.Camera.NearPlane;

        Segment3D sX = new Segment3D(0, 0, 0, 1, 0, 0);
        Segment3D sY = new Segment3D(0, 0, 0, 0, 1, 0);
        Segment3D sZ = new Segment3D(0, 0, 0, 0, 0, 1);

        // Compute the intersections with the camera planes
        if (!Vector3D.AreParallel(viewport.Camera.ViewNormal, Vector3D.AxisX))
        {
            sX.IntersectWith(nearPlane, true, out ptXNear);
            sX.IntersectWith(farPlane, true, out ptXFar);
            if (ptXNear == null)
            {
                ptXNear = new Point3D(-1e10, 0, 0);
                ptXFar = new Point3D(1e10, 0, 0);

            }
        }
        else
        {
            ptXNear = null;
            ptXFar = null;
        }

        if (!Vector3D.AreParallel(viewport.Camera.ViewNormal, Vector3D.AxisY))
        {
            sY.IntersectWith(nearPlane, true, out ptYNear);
            sY.IntersectWith(farPlane, true, out ptYFar);
            if (ptYNear == null)
            {
                ptYNear = new Point3D(0, -1e10, 0);
                ptYFar = new Point3D(0, 1e10, 0);
            }
        }
        else
        {
            ptYNear = null;
            ptYFar = null;
        }
        
        if (!Vector3D.AreParallel(viewport.Camera.ViewNormal, Vector3D.AxisZ))
        {
            sZ.IntersectWith(nearPlane, true, out ptZNear);
            sZ.IntersectWith(farPlane, true, out ptZFar);
            if (ptZNear == null)
            {
                ptZNear = new Point3D(0, 0, -1e10);
                ptZFar = new Point3D(0, 0, 1e10);
            }
        }
        else
        {
            ptZNear = null;
            ptZFar = null;
        }
    }

    protected override void DrawViewport(DrawSceneParams myParams)
    {
        base.DrawViewport(myParams);
        var rc = myParams.RenderContext;

        rc.SetShader(shaderType.NoLights);

        // Draw the 3D lines between the camera planes

        if (ptXNear != null && ptXFar != null)
        {
            rc.SetColorWireframe(AxisXColor);
            rc.DrawLines(new Point3D[] { ptXNear, ptXFar });
        }

        if (ptYNear != null && ptYFar != null)
        {
            rc.SetColorWireframe(AxisYColor);
            rc.DrawLines(new Point3D[] { ptYNear, ptYFar });
        }

        if (ptZNear != null && ptZFar != null)
        {
            rc.SetColorWireframe(AxisZColor);
            rc.DrawLines(new Point3D[] { ptZNear, ptZFar });
        }
    }

    protected override void DrawOverlay(DrawSceneParams data)
    {
        // Draw in 2D the parts of the lines before the near camera plane
        data.RenderContext.SetShader(shaderType.NoLights);

        for (int i = 0; i < Viewports.Count; i++)
        {
            ComputeNearFarIntersections(Viewports[i]);

            // X axis 
            DrawLinesBeforeNear(Viewports[i], ptXNear, ptXFar, AxisXColor);
            // Y axis 
            DrawLinesBeforeNear(Viewports[i], ptYNear, ptYFar, AxisYColor);
            // Z axis 
            DrawLinesBeforeNear(Viewports[i], ptZNear, ptZFar, AxisZColor);
        }
    }

    private void DrawLinesBeyondFar(IViewport viewport, Point3D nearPt, Point3D farPt, Color color)
    {
        if (farPt == null || nearPt == null)
            return;

        Vector3D dir = Vector3D.Subtract(farPt, nearPt);
        dir.Normalize();

        if (viewport == null)
            viewport = ActiveViewport;

        Point3D pt1 = viewport.WorldToScreen(farPt);
        Point3D pt2 = viewport.WorldToScreen(farPt + dir);
        DrawLine(viewport, pt1, pt2, color, true);
    }

    private void DrawLinesBeforeNear(Viewport viewport, Point3D nearPt, Point3D farPt, Color color)
    {
        if (farPt == null || nearPt == null)
            return;

        Vector3D dir = Vector3D.Subtract(farPt, nearPt);
        dir.Normalize();

        if (viewport == null)
            viewport = ActiveViewport;

        var pt1 = viewport.WorldToScreen(nearPt);
        var pt2 = viewport.WorldToScreen(nearPt - dir);

        DrawLine(viewport, pt1, pt2, color, false);
    }

    private void DrawLine(IViewport viewport, Point3D pt1, Point3D pt2, Color color, bool convertToViewport)
    {
        if (pt1 == null || pt2 == null)
            return;

        Segment2D screenLine = new Segment2D(pt1, pt2);

        int[] viewFrame = viewport.GetViewFrame();

        double left = viewFrame[0];
        double right = viewFrame[0] + viewFrame[2];
        double bottom = viewFrame[1];
        double top = viewFrame[1] + viewFrame[3] - 1;
        Point2D lowerLeft = new Point2D(left, bottom);
        Point2D lowerRight = new Point2D(right, bottom);
        Point2D upperLeft = new Point2D(left, top);
        Point2D upperRight = new Point2D(right, top);

        Segment2D[] screenLines = new Segment2D[]
        {
            new Segment2D(lowerLeft, lowerRight),
            new Segment2D(upperLeft, upperRight),
            new Segment2D(lowerLeft, upperLeft),
            new Segment2D(lowerRight, upperRight)
        };

        Point2D ptAxis1 = null, ptAxis2 = null;

        Vector2D dir = Vector2D.Subtract(pt2, pt1);
        dir.Normalize();

        // Extend the segment outside the window limits
        screenLine.P1 = screenLine.P0 + dir * (viewport.Size.Width + viewport.Size.Height);

        // Compute the intersection of the screen line against the lower and upper border of the viewport 
        Segment2D.Intersection(screenLine, screenLines[0], out ptAxis1);
        Segment2D.Intersection(screenLine, screenLines[1], out ptAxis2);

        if (ptAxis1 != null)
            screenLine.P1 = ptAxis1;

        if (ptAxis2 != null)
            screenLine.P1 = ptAxis2;

        // Compute the intersection of the infinite screen line against the left and right border of the viewport 
        Segment2D.Intersection(screenLine, screenLines[2], out ptAxis1);
        Segment2D.Intersection(screenLine, screenLines[3], out ptAxis2);

        if (ptAxis1 != null)
            screenLine.P1 = ptAxis1;

        if (ptAxis2 != null)
            screenLine.P1 = ptAxis2;

        RenderContext.SetLineSize(1);
        RenderContext.EnableThickLines();
        RenderContext.SetColorWireframe(color);

        var tol = 1e-6;

        // Consider some tolerance
        if (screenLine.P0.X >= left - tol && screenLine.P0.X <= right + tol &&
            screenLine.P0.Y >= bottom - tol && screenLine.P0.Y <= top + tol &&
            screenLine.P1.X >= left - tol && screenLine.P1.X <= right + tol &&
            screenLine.P1.Y >= bottom - tol && screenLine.P1.Y <= top + tol)
        {
            if (convertToViewport)
            {
                // When drawing the lines beyond far inside the DrawViewportBackground, the camera is set to just the Viewport, not to the whole ViewportLayout,
                // so if we have multiple viewports we must adjust the screen coordinates
                screenLine.P0 = new Point2D(screenLine.P0.X - left, screenLine.P0.Y - bottom);
                screenLine.P1 = new Point2D(screenLine.P1.X - left, screenLine.P1.Y - bottom);
            }

            RenderContext.DrawLines(new float[]
            {
                (float) screenLine.P0.X, (float) screenLine.P0.Y, 0,
                (float) screenLine.P1.X, (float) screenLine.P1.Y, 0
            });
        }
    }
}
Was this article helpful?
1 out of 1 found this helpful

Comments

0 comments

Please sign in to leave a comment.

Articles in this section

See more