Ejemplo n.º 1
0
    public static Vector3 random_cosine_direction()
    {
        float r1  = Random48.Get();
        float r2  = Random48.Get();
        float z   = Mathf.Sqrt(1 - r2);
        float phi = 2 * 3.141592f * r1;
        float x   = Mathf.Cos(phi) * Mathf.Sqrt(r2);
        float y   = Mathf.Sin(phi) * Mathf.Sqrt(r2);

        return(new Vector3(x, y, z));
    }
Ejemplo n.º 2
0
 public Vector3 generate(Vector3 origin)
 {
     if (Random48.Get() < 0.5)
     {
         return(random(origin));
     }
     else
     {
         return(random(origin));
     }
 }
    bool refractcal(Ray MyRay, RaycastHit MyRayHit, ref Ray scattered, float reffactor, bool forceRefract)
    {
        Vector3 out_normal = Vector3.zero;
        Vector3 reflected  = reflect(MyRay.direction, MyRayHit.normal);
        float   realfactor = 1.5f;
        Vector3 refracted  = Vector3.zero;
        float   reflectp   = 0f;
        float   cosine     = 0f;

        if (Vector3.Dot(MyRay.direction, MyRayHit.normal) > 0)
        {
            out_normal = -MyRayHit.normal;
            realfactor = reffactor;
            cosine     = reffactor * Vector3.Dot(MyRay.direction, MyRayHit.normal) / MyRay.direction.magnitude;
        }
        else
        {
            out_normal = MyRayHit.normal;
            realfactor = 1.0f / reffactor;
            cosine     = -Vector3.Dot(MyRay.direction, MyRayHit.normal) / MyRay.direction.magnitude;
        }
        if (refract(MyRay.direction, out_normal, realfactor, ref refracted))
        {
            float r = (1f - reffactor) / (1f + reffactor);
            r        = r * r;
            reflectp = r + (1 - r) * Mathf.Pow((1 - cosine), 5);
            if (forceRefract)
            {
                scattered = new Ray(MyRayHit.point, refracted);
                return(true);
            }
        }
        else
        {
            if (forceRefract)
            {
                scattered = new Ray(MyRayHit.point, refracted);
                return(true);
            }
            scattered = new Ray(MyRayHit.point, reflected);
            reflectp  = 1.0f;
        }
        if (Random48.Get() < reflectp)
        {
            scattered = new Ray(MyRayHit.point, reflected);
            return(false);
        }
        else
        {
            scattered = new Ray(MyRayHit.point, refracted);
            return(true);
        }
    }
        // Start is called before the first frame update
        void Start()
        {
            query     = new KDQuery();
            VPLVector = new Vector3[InitVPLCount];
            VPLPoints = new Vector3[InitVPLCount];
            SpotAngle = GetComponent <Light>().spotAngle;
            if (type == "Point Light")
            {
                Vector3    org = this.transform.position;
                RaycastHit hit;
                parent = new GameObject();
                for (int i = 0; i < InitVPLCount; i++)
                {
                    float Rx = Random48.Get();
                    float Ry = Random48.Get();
                    float Rz = Random48.Get();
                    Rx        = halton(seedCount, 2);
                    Ry        = halton(seedCount, 3);
                    Rz        = halton(seedCount++, 4);
                    seedCount = seedCount % (maxSeed * 3);
                    Vector3 tempDir = new Vector3(Rx * 2.0f - 1.0f, Ry * 2.0f - 1.0f, Rz * 2.0f - 1.0f);
                    //tempDir.Normalize();
                    //tempDir -= new Vector3(0.5f,0.5f,0.5f);
                    VPLVector[i] = (tempDir);
                    Vector3 B   = tempDir;
                    Vector3 dir = this.transform.right * B.x + this.transform.up * B.y - this.transform.forward * B.z;
                    if (Physics.Raycast(org, dir, out hit, 1000))
                    {
                        VPLPoints[i] = (hit.point);
                    }
                    GameObject temp = new GameObject();
                    temp.transform.parent   = parent.transform;
                    temp.transform.position = VPLPoints[i];
                    temp.AddComponent <Light>();
                    temp.GetComponent <Light>().shadows          = LightShadows.Hard;
                    temp.GetComponent <Light>().shadowBias       = 0;
                    temp.GetComponent <Light>().shadowNormalBias = 0;
                    temp.GetComponent <Light>().shadowNearPlane  = 0.2f;
                    Vector3 CubeUV = convert_xyz_to_cube_uv(B);
                    if (CubeUV.x == 0.0f)
                    {
                        temp.GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.PositiveX, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                    }
                    if (CubeUV.x == 1.0f)
                    {
                        temp.GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.NegativeX, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                    }
                    if (CubeUV.x == 2.0f)
                    {
                        temp.GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.PositiveY, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                    }
                    if (CubeUV.x == 3.0f)
                    {
                        temp.GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.NegativeY, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                    }
                    if (CubeUV.x == 4.0f)
                    {
                        temp.GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.PositiveZ, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                    }
                    if (CubeUV.x == 5.0f)
                    {
                        temp.GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.NegativeZ, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                    }
                    VPL.Add(temp);
                }
            }
            else
            {
                int        PassCount = 0;
                Vector3    org       = this.transform.position;
                RaycastHit hit;
                Texture2D  tempTex = new Texture2D(1024, 1024);
                RenderTexture.active = rendertexture;
                tempTex.ReadPixels(new UnityEngine.Rect(0, 0, 1024, 1024), 0, 0);
                parent = new GameObject();
                Debug.DrawLine(transform.position + transform.forward * 10, this.transform.position);
                while (PassCount != InitVPLCount)
                {
                    //float Rx = ((float)Random.Range(0, 1024)) / 1024.0f;
                    //float Ry = ((float)Random.Range(0, 1024)) / 1024.0f;

                    float Rx = Random48.Get();
                    float Ry = Random48.Get();
                    Rx        = halton(seedCount, 2);
                    Ry        = halton(seedCount++, 3);
                    seedCount = seedCount % (maxSeed * 2);
                    if (Mathf.Sqrt((Rx - 0.5f) * (Rx - 0.5f) + (Ry - 0.5f) * (Ry - 0.5f)) <= radius)
                    {
                        Vector3 B = new Vector2(Rx, Ry);
                        B.x -= 0.5f;
                        B.y -= 0.5f;
                        VPLVector[PassCount] = B;
                        //            float l = Vector3.Dot(B, B);
                        float z;
                        z = -Mathf.Tan((Fov / 2.0f) / 180.0f * 3.1415926f);

                        Vector3 dir = this.transform.right * B.x + this.transform.up * B.y - this.transform.forward * z;

                        if (Physics.Raycast(org, dir, out hit, 1000))
                        {
                            Vector3 p;
                            p.x = B.x * 1024.0f;
                            p.y = B.y * 1024.0f;

                            GameObject temp = new GameObject();
                            temp.transform.parent   = parent.transform;
                            temp.transform.position = hit.point;
                            temp.AddComponent <Light>();
                            temp.GetComponent <Light>().color   = (tempTex.GetPixel((int)p.x, (int)p.y));
                            temp.GetComponent <Light>().shadows = LightShadows.Hard;
                            VPL.Add(temp);
                            VPLPoints[PassCount] = hit.point;
                            PassCount++;
                        }
                        else
                        {
                            continue;
                        }
                    }
                }
                int a = 1;
                tree = new KDTree(VPLPoints, 16);
            }
        }
        // Update is called once per frame
        void Update()
        {
            //VPL.Clear();
            //VPLPoints.Clear();
            //VPLVector.Clear();
            if (type == "Point Light")
            {
                Vector3    org = this.transform.position;
                RaycastHit hit;
                for (int i = 0; i < InitVPLCount; i++)
                {
                    Vector3 checkdir      = (VPLPoints[i] - org).normalized;
                    float   checkdistance = (VPLPoints[i] - org).magnitude;
                    if (Physics.Raycast(org, checkdir, out hit, checkdistance))
                    {
                        float Rx = Random48.Get();
                        float Ry = Random48.Get();
                        float Rz = Random48.Get();
                        Rx        = halton(seedCount, 2);
                        Ry        = halton(seedCount, 3);
                        Rz        = halton(seedCount++, 4);
                        seedCount = seedCount % (maxSeed * 3);
                        Vector3 tempDir = new Vector3(Rx * 2.0f - 1.0f, Ry * 2.0f - 1.0f, Rz * 2.0f - 1.0f);
                        tempDir.Normalize();
                        //tempDir -= new Vector3(0.5f, 0.5f, 0.5f);
                        VPLVector[i] = tempDir;
                        Vector3 B   = VPLVector[i];
                        Vector3 dir = this.transform.right * B.x + this.transform.up * B.y - this.transform.forward * B.z;
                        if (Physics.Raycast(org, dir, out hit, 1000))
                        {
                            VPLPoints[i] = (hit.point);
                            VPL[i].GetComponent <Transform>().LookAt(hit.normal);
                        }

                        VPL[i].transform.position = VPLPoints[i];

                        Vector3 CubeUV = convert_xyz_to_cube_uv(B);
                        if (CubeUV.x == 0.0f)
                        {
                            VPL[i].GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.PositiveX, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                        }
                        if (CubeUV.x == 1.0f)
                        {
                            VPL[i].GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.NegativeX, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                        }
                        if (CubeUV.x == 2.0f)
                        {
                            VPL[i].GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.PositiveY, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                        }
                        if (CubeUV.x == 3.0f)
                        {
                            VPL[i].GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.NegativeY, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                        }
                        if (CubeUV.x == 4.0f)
                        {
                            VPL[i].GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.PositiveZ, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                        }
                        if (CubeUV.x == 5.0f)
                        {
                            VPL[i].GetComponent <Light>().color = MyCubeMap.GetPixel(CubemapFace.NegativeZ, (int)(CubeUV.y * 1024.0), (int)(CubeUV.z * 1024.0));
                        }
                    }
                }
            }
            else
            {
                Vector3    org = this.transform.position;
                RaycastHit hit;
                Texture2D  tempTex = new Texture2D(1024, 1024);
                RenderTexture.active = rendertexture;
                tempTex.ReadPixels(new UnityEngine.Rect(0, 0, 1024, 1024), 0, 0);
                Vector3 temp = (this.transform.right + this.transform.up + this.transform.forward);
                Debug.DrawLine(transform.position + transform.forward * 10, this.transform.position);
                for (int i = 0; i < InitVPLCount; i++)
                {
                    Vector3 checkdir      = (VPL[i].transform.position - org).normalized;
                    float   checkdistance = (VPL[i].transform.position - org).magnitude;
                    float   checkAngle    = Vector3.Angle(checkdir, transform.forward);

                    if (Physics.Raycast(org, checkdir, out hit, checkdistance) || checkAngle > (SpotAngle / 2.0f))
                    {
                        float Rx = Random48.Get();
                        float Ry = Random48.Get();
                        Rx        = halton(seedCount, 2);
                        Ry        = halton(seedCount++, 3);
                        seedCount = seedCount % (maxSeed * 2);
                        if (Mathf.Sqrt((Rx - 0.5f) * (Rx - 0.5f) + (Ry - 0.5f) * (Ry - 0.5f)) <= radius)
                        {
                            Vector3 B = new Vector3(Rx, Ry, 0);

                            B.x -= 0.5f;
                            B.y -= 0.5f;
                            //            float l = Vector3.Dot(B, B);
                            VPLVector[i] = B;
                            float z;
                            z = -Mathf.Tan((Fov / 2.0f) / 180.0f * 3.1415926f);

                            Vector3 dir = this.transform.right * B.x + this.transform.up * B.y - this.transform.forward * z;
                            if (Physics.Raycast(org, dir, out hit, 1000))
                            {
                                Vector3 p;
                                p.x = B.x * 1024.0f;
                                p.y = B.y * 1024.0f;

                                VPL[i].transform.position = hit.point;
                                tree.Points[i]            = hit.point;
                                VPL[i].GetComponent <Transform>().LookAt(hit.normal);
                                VPL[i].GetComponent <Light>().color = (tempTex.GetPixel((int)p.x, (int)p.y));
                            }
                        }
                    }
                }
                tree.Rebuild();
                int count = 0;
                Dictionary <int, bool> lightflag = new Dictionary <int, bool>();
                for (int i = 0; i < InitVPLCount; i++)
                {
                    if (!lightflag.ContainsKey(i))
                    {
                        count++;
                        VPL[i].GetComponent <Light>().enabled = true;
                        lightflag[i] = true;
                        var resultIndices = new List <int>();
                        query.Radius(tree, tree.Points[i], Radius, resultIndices);
                        for (int j = 0; j < resultIndices.Count; j++)
                        {
                            if (resultIndices[j] != i)
                            {
                                VPL[resultIndices[j]].GetComponent <Light>().enabled = false;
                                lightflag[resultIndices[j]] = true;
                            }
                        }
                    }
                }
                VPL_Num = count.ToString();
                Destroy(tempTex);
            }

            if (Input.GetKeyDown(KeyCode.D))
            {
                DEB = !DEB;
                CANV.SetActive(DEB);
            }

            if (Input.GetKeyDown(KeyCode.E))
            {
                SDEB = !SDEB;
                CANV2.SetActive(SDEB);
            }


            if (DEB == true)
            {
                Vector3 TP = this.GetComponent <Transform>().position;
                for (int i = 0; i < InitVPLCount; i++)
                {
                    Debug.DrawLine(TP, VPL[i].GetComponent <Transform>().position);
                }
                if (Input.GetKeyDown(KeyCode.W))
                {
                    NowSeeLight++;
                    NowSeeLight = NowSeeLight % InitVPLCount;
                    MainCam.GetComponent <Transform>().position = VPL[NowSeeLight].GetComponent <Transform>().position;
                    MainCam.GetComponent <Transform>().rotation = VPL[NowSeeLight].GetComponent <Transform>().rotation;
                    //C.transform.parent = VPL[NowSeeLight].transform;
                    C.transform.GetComponent <Transform>().rotation = VPL[NowSeeLight].GetComponent <Transform>().rotation;
                    C.transform.GetComponent <Transform>().position = VPL[NowSeeLight].GetComponent <Transform>().position;
                }

                if (Input.GetKeyDown(KeyCode.S))
                {
                    NowSeeLight--;
                    if (NowSeeLight < 0)
                    {
                        NowSeeLight += InitVPLCount;
                    }
                    NowSeeLight = NowSeeLight % InitVPLCount;
                    MainCam.GetComponent <Transform>().position = VPL[NowSeeLight].GetComponent <Transform>().position;
                    MainCam.GetComponent <Transform>().rotation = VPL[NowSeeLight].GetComponent <Transform>().rotation;
                    //C.transform.parent = VPL[NowSeeLight].transform;
                    C.transform.GetComponent <Transform>().rotation = VPL[NowSeeLight].GetComponent <Transform>().rotation;
                    C.transform.GetComponent <Transform>().position = VPL[NowSeeLight].GetComponent <Transform>().position;
                }
            }
        }
Ejemplo n.º 6
0
    Color RHit(Ray Input, int BoundTime)
    {
        RaycastHit MYRayHit;
        Color      color = Color.black;

        //if (BoundTime >= texture_boundtime)
        //{
        //    Vector3 Ldir = ThisLight.GetComponent<Transform>().position - Input.origin;
        //    Ray NRay = new Ray(Input.origin, Ldir.normalized);
        //    if (Physics.Raycast(NRay, out MYRayHit, 2000f))
        //    {
        //        if (MYRayHit.collider.tag == "Light")//done
        //        {
        //            return Color.white * 4;
        //        }
        //    }
        //    return Color.black;
        //}


        if (Physics.Raycast(Input, out MYRayHit, 2000f) && (BoundTime < texture_boundtime))
        {
            Renderer renderer    = MYRayHit.collider.GetComponent <MeshRenderer>();
            Ray      scattered   = new Ray();
            Color    attenuation = Color.black;
            Color    emitted     = Color.black;
            float    scatter_pdf = 1f;
            float    pdf_val     = 1f;
            float    coef        = 1f;
            if (MYRayHit.collider.tag == "Light")//done
            {
                return(Color.white * 3);
            }
            else if (MYRayHit.collider.tag == "lambertian")
            {
                emitted = new Color(0.15f, 0.15f, 0.15f);
                //Vector3 scatter_direction = MYRayHit.normal + new Vector3(Random.Range(-1, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized;
                //scattered = new Ray(MYRayHit.point, scatter_direction);
                getPixelColor(MYRayHit, ref attenuation);
                attenuation *= renderer.material.color;
                Vector3[] onb = new Vector3[3];
                build_onb(MYRayHit.normal, ref onb);
                Vector3 cos_pdf   = cos_pdf_generate(onb);
                Vector3 light_pdf = lights.generate(MYRayHit.point);

                Vector3 scatter_dir = (Random.Range(0f, 1f) < 0.5f) ? cos_pdf : light_pdf;
                scattered = new Ray(MYRayHit.point, scatter_dir.normalized);

                float cos         = Vector3.Dot(scatter_dir.normalized, onb[2]);
                float cos_value   = (cos > 0) ? (cos / 3.141592f) : 0;
                float light_value = lights.pdf_value(Input, MYRayHit.point, scatter_dir.normalized);
                pdf_val     = 0.5f * cos_value + 0.5f * light_value;
                scatter_pdf = (cos < 0) ? 0 : (Vector3.Dot(MYRayHit.normal, scatter_dir.normalized) / 3.141592f);
                coef        = scatter_pdf / pdf_val;
                if (coef > 1.2)
                {
                    coef = 1.2f;
                }
                //scatter_pdf *= 1.2f;
            }
            else if (MYRayHit.collider.tag == "metal")
            {
                float fuzz = 0.01f;
                emitted = Color.black;
                Vector3 reflected = Vector3.Reflect(Input.direction.normalized, MYRayHit.normal);
                scattered = new Ray(MYRayHit.point, reflected + fuzz * Random48.random_cosine_direction());
                Vector3 outNor = MYRayHit.normal;
                getPixelNormal(MYRayHit, ref outNor);
                Vector3 reflectedn = Vector3.Reflect(Input.direction.normalized, outNor);
                scattered = new Ray(MYRayHit.point, reflected + fuzz * new Vector3(Random.Range(-1, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized);
                getPixelColor(MYRayHit, ref attenuation);
                attenuation *= renderer.material.color;
                if (Vector3.Dot(scattered.direction, MYRayHit.normal) > 0)
                {
                }
                else
                {
                    return(Color.black);
                }
            }
            else if (MYRayHit.collider.tag == "dielectric")
            {
                Vector3 out_normal = Vector3.zero;
                Vector3 reflected  = reflect(Input.direction, MYRayHit.normal);
                float   reffactor  = 1.5f;
                float   realfactor = 1.5f;
                attenuation = Color.white;
                Vector3 refracted = Vector3.zero;
                float   reflectp  = 0f;
                float   cosine    = 0f;
                if (Vector3.Dot(Input.direction, MYRayHit.normal) > 0)
                {
                    out_normal = -MYRayHit.normal;
                    realfactor = reffactor;
                    cosine     = reffactor * Vector3.Dot(Input.direction, MYRayHit.normal) / Input.direction.magnitude;
                }
                else
                {
                    out_normal = MYRayHit.normal;
                    realfactor = 1.0f / reffactor;
                    cosine     = -Vector3.Dot(Input.direction, MYRayHit.normal) / Input.direction.magnitude;
                }
                if (refract(Input.direction, out_normal, realfactor, ref refracted))
                {
                    float r = (1f - reffactor) / (1f + reffactor);
                    r        = r * r;
                    reflectp = r + (1 - r) * Mathf.Pow((1 - cosine), 5);
                }
                else
                {
                    //scattered = new Ray(MYRayHit.point, reflected);
                    reflectp = 1.0f;
                }
                if (Random48.Get() < reflectp)
                {
                    scattered = new Ray(MYRayHit.point, reflected);
                }
                else
                {
                    scattered = new Ray(MYRayHit.point, refracted);
                }
                emitted = Color.black;
            }
            return(emitted + attenuation * RHit(scattered, ++BoundTime) * coef);
        }
        else
        {
            return(Color.black);
        }
    }
Ejemplo n.º 7
0
    void RayCastPass(Ray INPUT, int texture_x, int texture_y, int texture_sample)
    {
        RaycastHit MYRayHit;

        if (Physics.Raycast(INPUT, out MYRayHit, 2000f))
        {
            Renderer renderer = MYRayHit.collider.GetComponent <MeshRenderer>();

            if (MYRayHit.collider.tag == "Light")//done
            {
                HitPoint_position[texture_x, texture_y, texture_sample] = MYRayHit.point;
            }
            else if (MYRayHit.collider.tag == "lambertian")
            {
                HitPoint_position[texture_x, texture_y, texture_sample] = MYRayHit.point;
            }
            else if (MYRayHit.collider.tag == "metal")
            {
                float   fuzz      = 0.05f;
                Vector3 reflected = Vector3.Reflect(INPUT.direction.normalized, MYRayHit.normal);
                Ray     scattered = new Ray(MYRayHit.point, reflected + fuzz * new Vector3(Random.Range(-1, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f)).normalized);
                if (Vector3.Dot(scattered.direction, MYRayHit.normal) > 0)
                {
                    RayCastPass(scattered, texture_x, texture_y, texture_sample);
                }
            }
            else if (MYRayHit.collider.tag == "dielectric")
            {
                Vector3 out_normal = Vector3.zero;
                Ray     scattered  = new Ray(new Vector3(0, 0, 0), new Vector3(0, 0, 1));
                Vector3 reflected  = reflect(INPUT.direction, MYRayHit.normal);
                float   reffactor  = 1.5f;
                float   realfactor = 1.5f;
                Vector3 refracted  = Vector3.zero;
                float   reflectp   = 0f;
                float   cosine     = 0f;
                if (Vector3.Dot(INPUT.direction, MYRayHit.normal) > 0)
                {
                    out_normal = -MYRayHit.normal;
                    realfactor = reffactor;
                    cosine     = reffactor * Vector3.Dot(INPUT.direction, MYRayHit.normal) / INPUT.direction.magnitude;
                }
                else
                {
                    out_normal = MYRayHit.normal;
                    realfactor = 1.0f / reffactor;
                    cosine     = -Vector3.Dot(INPUT.direction, MYRayHit.normal) / INPUT.direction.magnitude;
                }
                if (refract(INPUT.direction, out_normal, realfactor, ref refracted))
                {
                    float r = (1f - reffactor) / (1f + reffactor);
                    r        = r * r;
                    reflectp = r + (1 - r) * Mathf.Pow((1 - cosine), 5);
                }
                else
                {
                    scattered = new Ray(MYRayHit.point, reflected);
                    reflectp  = 1.0f;
                }
                if (Random48.Get() < reflectp)
                {
                    scattered = new Ray(MYRayHit.point, reflected);
                }
                else
                {
                    scattered = new Ray(MYRayHit.point, refracted);
                }
                RayCastPass(scattered, texture_x, texture_y, texture_sample);
            }
        }
    }