2011-12-10

Mapowanie tekstury na torus

Z związku z pojawieniem się powierzchni wyższego rzędu przypominających torus dodałem osobną klasę mapowania na torus, której kod bardzo przypomina ten z torusa.

Różnice są na początku. Ponieważ z założenie mapować możemy nie tylko na torus, ale na dowolny kształt nie możemy zakładać, że punkty będą leżeć dokładnie na torusie. Dodatkowo do policzenia mapowania potrzebujemy punkt w lokalnych współrzędnych torusa (tak jak liczyliśmy to dla torusa) zaś tutaj dostajemy go we współrzędnych UVW. Pierwszych kilka linijek liczy więc lokalną współrzędną i mały promień związany z tym punktem na lub poza torusem.

public class TorodidalUVMapper : UVMapper
{
    private double m_local_small_radius;
    private double m_local_big_radius;
    private double m_big_radius;
    private double m_small_radius;

    [YAXNode]
    public double BigRadius
    {
        get
        {
            return m_big_radius;
        }
        set
        {
            m_big_radius = value;
            UpdateLocalRadiuses();
        }
    }

    [YAXNode]
    public double SmallRadius
    {
        get
        {
            return m_small_radius;
        }
        set
        {
            m_small_radius = value;
            UpdateLocalRadiuses();
        }
    }

    [YAXOnDeserialized]
    private void UpdateLocalRadiuses()
    {
        m_local_small_radius = SmallRadius / (SmallRadius + BigRadius);
        m_local_big_radius = BigRadius / (SmallRadius + BigRadius);
    }

    public TorodidalUVMapper(double a_big_radius = 0, double a_small_radius = 0)
    {
        BigRadius = a_big_radius;
        SmallRadius = a_small_radius;
    }

    public override Vector2 Map(Vector3 a_uvw)
    {
        Vector3 local_pos = (a_uvw - Vector3.HALF) / new Vector3(0.5, 0.5 / 
            m_local_small_radius, 0.5);
        Vector3 p1 = new Vector3(local_pos.X, 0, local_pos.Z).Normalized * 
            m_local_big_radius;
        double small_radius = (local_pos - p1).Length;

        double ca = local_pos.Y / small_radius;

        Debug.Assert(ca.IsAlmostRelativeGreaterOrEqualThen(-1));
        Debug.Assert(ca.IsAlmostRelativeLessOrEqualThen(1));
        ca = DoubleExtensions.Limit(ca, -1, 1);

        double v = Math.Acos(ca) / Constants.PI / 2;

        if (local_pos.X * local_pos.X + local_pos.Z * local_pos.Z > 
            m_local_big_radius * m_local_big_radius)
        {
            v = v + 0.25;
        }
        else
        {
            if (local_pos.Y > 0)
                v = 0.25 - v;
            else
                v = 1.25 - v;
        }

        double u = Math.Atan2(local_pos.X, local_pos.Z) / (2 * Constants.PI);

        if (local_pos.X < 0)
            u = u + 1;

        return new Vector2(u, v);
    }

    public override UVMappingMethod Method
    {
        get
        {
            return UVMappingMethod.Toroidal;
        }
    }

    public override string ToString()
    {
        return String.Format("Toroidal uv mapper: R: {0}, r: {1}", BigRadius, 
             SmallRadius);
    }
}

Brak komentarzy:

Prześlij komentarz