Ejemplo n.º 1
0
        //Add vertex to mesh, normalize position to be on unit sphere.
        private ushort AddNormalizedVertex(float3 p)
        {
            p = p.Normalize();
            _sphereVertices.Add(p);

            return(_newVertIndex++);
        }
Ejemplo n.º 2
0
        public void     GetSamplingDirection(float _U1, float _U2, ref float3 _direction)
        {
            float theta = Mathf.Asin(Mathf.Sqrt(_U1));
//			float	theta = Mathf.Acos( Mathf.Sqrt( _U1 ) );
            float  phi = Mathf.TWOPI * _U2;
            float3 D   = new float3(Mathf.Sin(theta) * Mathf.Cos(phi), Mathf.Sin(theta) * Mathf.Sin(phi), Mathf.Cos(theta));

            Transform(M, D, ref _direction);

            _direction.Normalize();
        }
Ejemplo n.º 3
0
        public void     GetSamplingDirection(float _U1, float _U2, ref float3 _direction)
        {
//			float	theta = Mathf.Asin( Mathf.Sqrt( _U1 ) );
            float theta = Mathf.Acos(Mathf.Sqrt(_U1));
            float phi   = Mathf.TWOPI * _U2;

//          #if FIT_TRANSPOSED
//              Transform( new float3( Mathf.Sin(theta)*Mathf.Cos(phi), Mathf.Sin(theta)*Mathf.Sin(phi), Mathf.Cos(theta) ), M, ref _direction );
//          #else
            Transform(M, new float3(Mathf.Sin(theta) * Mathf.Cos(phi), Mathf.Sin(theta) * Mathf.Sin(phi), Mathf.Cos(theta)), ref _direction);
//          #endif
            _direction.Normalize();
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Calculates the vertex position so that it is parallel to the x-y plane.
        /// </summary>
        /// <param name="vertPos">Original vertex position.</param>
        /// <param name="normal">The normal of the polygon the vertex belongs to. Used as new Z axis.</param>
        /// <returns></returns>
        internal static float3 Reduce2D(this float3 vertPos, float3 normal)
        {
            normal = normal.Normalize(); //New z axis

            //If the normal equals the z axis of the world coodrinate system: reflect the point on the y axis.
            if (normal == float3.UnitZ)
            {
                var rot = new float3x3(-1, 0, 0,
                                       0, 1, 0,
                                       0, 0, 1);
                return(vertPos * rot);
            }

            //If the normal equals the negative z axis of the world coodrinate system: use the original coordinates.
            if (normal == float3.UnitZ * -1)
            {
                return(vertPos);
            }

            var v2 = float3.Cross(normal, float3.UnitZ); //rotation axis - new x axis

            v2 = v2.Normalize();
            var v3 = float3.Cross(normal, v2); //new y axis

            v3 = v3.Normalize();

            //Calculate change-of-basis matrix (orthonormal matrix).
            var row1 = new float3(v3.x, v2.x, normal.x);
            var row2 = new float3(v3.y, v2.y, normal.y);
            var row3 = new float3(v3.z, v2.z, normal.z);

            //vector in new basis * changeOfBasisMat = vector in old basis
            var changeOfBasisMat = new float3x3(row1, row2, row3);

            //In an orthonomal matrix the inverse equals the transpose, thus the transpose can be used to calculate vector in new basis (transpose * vector = vector in new basis).
            var transposeMat = new float3x3(changeOfBasisMat.Row0, changeOfBasisMat.Row1, changeOfBasisMat.Row2);

            transposeMat = transposeMat.Transpose();

            var newVert = transposeMat * vertPos;


            //Round, to get rid of potential exponent representation.
            var vecX = System.Math.Round(newVert.x, 5);
            var vecY = System.Math.Round(newVert.y, 5);
            var vecZ = System.Math.Round(newVert.z, 5);

            newVert = new float3((float)vecX, (float)vecY, (float)vecZ);

            return(newVert);
        }
Ejemplo n.º 5
0
        void    LoadNormalMap(System.IO.FileInfo _normalMapFileName)
        {
            try {
                ImageFile tempImageNormal = new ImageFile(_normalMapFileName);

                // Make sure we accept the image
                double isPOT = Math.Log(tempImageNormal.Width) / Math.Log(2.0);
                if (tempImageNormal.Width != tempImageNormal.Height ||
                    (int)isPOT != isPOT)
                {
                    throw new Exception("The converter only supports square power-of-two textures!\r\nThis image is " + tempImageNormal.Width + "x" + tempImageNormal.Height);
                }

                // Replace existing
                if (m_imageNormal != null)
                {
                    m_imageNormal.Dispose();
                    m_imageNormal = null;
                }
                m_imageNormal = tempImageNormal;

                imagePanelNormal.Bitmap = m_imageNormal.AsBitmap;

                // Read normals
                m_size = m_imageNormal.Width;
                nx     = new Complex[m_size, m_size];
                ny     = new Complex[m_size, m_size];

                const float pipo = 1.0f;                        // Amplification test => no real change!

                float3 normal = float3.Zero;
                m_imageNormal.ReadPixels((uint X, uint Y, ref float4 _color) => {
                    normal.Set(pipo * (2.0f * _color.x - 1.0f), pipo * (2.0f * _color.y - 1.0f), 2.0f * _color.z - 1.0f);
                    normal.Normalize();

                    double fact = normal.z != 0.0 ? 1.0 / normal.z : 0.0;
                    nx[X, Y].Set(fact * normal.x, 0);
                    ny[X, Y].Set(fact * normal.y, 0);
                });

                // Enable conversion
                buttonConvert.Enabled         = true;
                buttonConvertOneSided.Enabled = true;
            } catch (Exception _e) {
                MessageBox("An error occurred while loading the normal map:\r\n\r\n" + _e.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
Ejemplo n.º 6
0
        //Newell's Method - see Graphics Gems III, p. 232.
        /// <summary>
        /// Calculates a face normal from three vertices. The vertices have to be coplanar and part of the face.
        /// </summary>
        /// <param name="faceOuterVertices">All vertices of the outer boundary of the face.</param>
        /// <returns></returns>
        public static float3 CalculateFaceNormal(IList <Vertex> faceOuterVertices)
        {
            var normal = new float3();

            for (var i = 0; i < faceOuterVertices.Count; i++)
            {
                var vCur  = faceOuterVertices[i].VertData.Pos;
                var vNext = faceOuterVertices[(i + 1) % faceOuterVertices.Count].VertData.Pos;

                normal.x += (vCur.y - vNext.y) * (vCur.z + vNext.z);
                normal.y += (vCur.z - vNext.z) * (vCur.x + vNext.x);
                normal.z += (vCur.x - vNext.x) * (vCur.y + vNext.y);
            }
            normal = normal * -1;
            normal = normal.Normalize();

            return(normal);
        }
Ejemplo n.º 7
0
 private void RenderWithLights(Mesh rm, ShaderEffect effect)
 {
     if (_lights.Count > 0)
     {
         foreach (LightInfo li in _lights)
         {
             // SetupLight(li);
             effect.RenderMesh(rm);
         }
     }
     else
     {
         // No light present - switch on standard light
         effect.SetEffectParam(ShaderCodeBuilder.LightColorName, new float3(1, 1, 1));
         // float4 lightDirHom = new float4(0, 0, -1, 0);
         float4 lightDirHom = _rc.InvModelView * new float4(0, 0, -1, 0);
         // float4 lightDirHom = _rc.TransModelView * new float4(0, 0, -1, 0);
         float3 lightDir = lightDirHom.xyz;
         lightDir.Normalize();
         effect.SetEffectParam(ShaderCodeBuilder.LightDirectionName, lightDir);
         effect.SetEffectParam(ShaderCodeBuilder.LightIntensityName, (float)1);
         effect.RenderMesh(rm);
     }
 }
Ejemplo n.º 8
0
    bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray scattered, out float3 outLightE, ref int inoutRayCount, ref uint state)
    {
        outLightE = new float3(0, 0, 0);
        if (mat.type == Material.Type.Lambert)
        {
            // random point inside unit sphere that is tangent to the hit point
            float3 target = rec.pos + rec.normal + MathUtil.RandomUnitVector(ref state);
            scattered   = new Ray(rec.pos, float3.Normalize(target - rec.pos));
            attenuation = mat.albedo;

            // sample lights
#if DO_LIGHT_SAMPLING
            for (int i = 0; i < s_Spheres.Length; ++i)
            {
                if (!s_SphereMats[i].HasEmission)
                {
                    continue; // skip non-emissive
                }
                //@TODO if (&mat == &smat)
                //    continue; // skip self
                var s = s_Spheres[i];

                // create a random direction towards sphere
                // coord system for sampling: sw, su, sv
                float3 sw = Normalize(s.center - rec.pos);
                float3 su = Normalize(Cross(MathF.Abs(sw.x) > 0.01f ? new float3(0, 1, 0) : new float3(1, 0, 0), sw));
                float3 sv = Cross(sw, su);
                // sample sphere by solid angle
                float  cosAMax = MathF.Sqrt(MathF.Max(0.0f, 1.0f - s.radius * s.radius / (rec.pos - s.center).SqLength));
                float  eps1 = RandomFloat01(ref state), eps2 = RandomFloat01(ref state);
                float  cosA = 1.0f - eps1 + eps1 * cosAMax;
                float  sinA = MathF.Sqrt(1.0f - cosA * cosA);
                float  phi  = 2 * PI * eps2;
                float3 l    = su * MathF.Cos(phi) * sinA + sv * MathF.Sin(phi) * sinA + sw * cosA;
                l.Normalize();

                // shoot shadow ray
                Hit lightHit = default(Hit);
                int hitID    = 0;
                ++inoutRayCount;
                if (HitWorld(new Ray(rec.pos, l), kMinT, kMaxT, ref lightHit, ref hitID) && hitID == i)
                {
                    float omega = 2 * PI * (1 - cosAMax);

                    float3 rdir = r_in.dir;
                    Debug.Assert(rdir.IsNormalized);
                    float3 nl = Dot(rec.normal, rdir) < 0 ? rec.normal : -rec.normal;
                    outLightE += (mat.albedo * s_SphereMats[i].emissive) * (MathF.Max(0.0f, Dot(l, nl)) * omega / PI);
                }
            }
#endif
            return(true);
        }
        else if (mat.type == Material.Type.Metal)
        {
            Debug.Assert(r_in.dir.IsNormalized); Debug.Assert(rec.normal.IsNormalized);
            float3 refl = Reflect(r_in.dir, rec.normal);
            // reflected ray, and random inside of sphere based on roughness
            scattered   = new Ray(rec.pos, Normalize(refl + mat.roughness * RandomInUnitSphere(ref state)));
            attenuation = mat.albedo;
            return(Dot(scattered.dir, rec.normal) > 0);
        }
        else if (mat.type == Material.Type.Dielectric)
        {
            Debug.Assert(r_in.dir.IsNormalized); Debug.Assert(rec.normal.IsNormalized);
            float3 outwardN;
            float3 rdir = r_in.dir;
            float3 refl = Reflect(rdir, rec.normal);
            float  nint;
            attenuation = new float3(1, 1, 1);
            float3 refr;
            float  reflProb;
            float  cosine;
            if (Dot(rdir, rec.normal) > 0)
            {
                outwardN = -rec.normal;
                nint     = mat.ri;
                cosine   = mat.ri * Dot(rdir, rec.normal);
            }
            else
            {
                outwardN = rec.normal;
                nint     = 1.0f / mat.ri;
                cosine   = -Dot(rdir, rec.normal);
            }
            if (Refract(rdir, outwardN, nint, out refr))
            {
                reflProb = Schlick(cosine, mat.ri);
            }
            else
            {
                reflProb = 1;
            }
            if (RandomFloat01(ref state) < reflProb)
            {
                scattered = new Ray(rec.pos, Normalize(refl));
            }
            else
            {
                scattered = new Ray(rec.pos, Normalize(refr));
            }
        }
        else
        {
            attenuation = new float3(1, 0, 1);
            scattered   = default(Ray);
            return(false);
        }
        return(true);
    }
Ejemplo n.º 9
0
 /// <summary>
 /// Extrudes a given Face along a specified Vector.
 /// </summary>
 /// <param name="geometry"></param>
 /// <param name="faceHandle"></param>
 /// <param name="offset"></param>
 /// <param name="extrusionVector"></param>
 /// <returns></returns>
 public static Geometry ExtrudeFace(this Geometry geometry, int faceHandle, float offset, float3 extrusionVector)
 {
     extrusionVector = extrusionVector.Normalize();
     return(ExtrudeFaceByHandle(geometry, faceHandle, offset, extrusionVector));
 }
Ejemplo n.º 10
0
        public void Normalize_Instance(float3 vec, float3 expected)
        {
            var actual = vec.Normalize();

            Assert.Equal(expected, actual);
        }
Ejemplo n.º 11
0
        public static Mesh CreateTorus(float segradius, float sliceradius, int segments, int slices)
        {
            float3[] verts = new float3[4 * segments * slices];     // one vertex per segment and one extra for the center point
            float3[] norms = new float3[verts.Length];              // one normal at each vertex
            ushort[] tris  = new ushort[3 * 2 * segments * slices]; // a triangle per segment. Each triangle is made of three indices

            float deltaSegment = 2 * M.Pi / segments;
            float deltaSlice   = 2 * M.Pi / slices;

            int triCount = 0;

            for (int i = 0; i < segments; i++)
            {
                for (int j = 0; j < slices; j++)
                {
                    float3 slicePoint = new float3(sliceradius * M.Cos(j * deltaSlice), sliceradius * M.Sin(j * deltaSlice), 0);
                    verts[4 * i * slices + 4 * j]     = new float3((segradius + slicePoint.x) * M.Cos(i * deltaSegment), slicePoint.y, (segradius + slicePoint.x) * M.Sin(i * deltaSegment));
                    verts[4 * i * slices + 4 * j + 1] = new float3((segradius + slicePoint.x) * M.Cos(i * deltaSegment), slicePoint.y, (segradius + slicePoint.x) * M.Sin(i * deltaSegment));
                    verts[4 * i * slices + 4 * j + 2] = new float3((segradius + slicePoint.x) * M.Cos(i * deltaSegment), slicePoint.y, (segradius + slicePoint.x) * M.Sin(i * deltaSegment));
                    verts[4 * i * slices + 4 * j + 3] = new float3((segradius + slicePoint.x) * M.Cos(i * deltaSegment), slicePoint.y, (segradius + slicePoint.x) * M.Sin(i * deltaSegment));

                    tris[triCount++] = (ushort)(4 * i * slices + 4 * j + 3);
                    tris[triCount++] = (ushort)(4 * ((i + 1) % segments) * slices + (4 * ((j + 1) % slices)));
                    tris[triCount++] = (ushort)(4 * i * slices + (4 * ((j + 1) % slices) + 1));

                    tris[triCount++] = (ushort)(4 * i * slices + 4 * j + 3);
                    tris[triCount++] = (ushort)(4 * ((i + 1) % segments) * slices + (4 * j + 2));
                    tris[triCount++] = (ushort)(4 * ((i + 1) % segments) * slices + (4 * ((j + 1) % slices)));

                    float3 sliceMidVecA = new float3(sliceradius * M.Cos((j + 0.5f) * deltaSlice), sliceradius * M.Sin((j + 0.5f) * deltaSlice), 0);
                    float3 sliceMidVecB = new float3(sliceradius * M.Cos((j - 0.5f) * deltaSlice), sliceradius * M.Sin((j - 0.5f) * deltaSlice), 0);
                    float3 segMidVecA   = new float3(segradius * M.Cos((i + 0.5f) * deltaSegment), 0, segradius * M.Sin((i + 0.5f) * deltaSegment));
                    float3 segMidVecB   = new float3(segradius * M.Cos((i - 0.5f) * deltaSegment), 0, segradius * M.Sin((i - 0.5f) * deltaSegment));

                    float3 point1 = new float3((segradius + sliceMidVecB.x) * M.Cos((i - 0.5f) * deltaSegment), sliceMidVecB.y, (segradius + sliceMidVecB.x) * M.Sin((i - 0.5f) * deltaSegment));
                    float3 point2 = new float3((segradius + sliceMidVecB.x) * M.Cos((i + 0.5f) * deltaSegment), sliceMidVecB.y, (segradius + sliceMidVecB.x) * M.Sin((i + 0.5f) * deltaSegment));
                    float3 point3 = new float3((segradius + sliceMidVecA.x) * M.Cos((i - 0.5f) * deltaSegment), sliceMidVecA.y, (segradius + sliceMidVecA.x) * M.Sin((i - 0.5f) * deltaSegment));
                    float3 point4 = new float3((segradius + sliceMidVecA.x) * M.Cos((i + 0.5f) * deltaSegment), sliceMidVecA.y, (segradius + sliceMidVecA.x) * M.Sin((i + 0.5f) * deltaSegment));


                    float3 v1 = (point1 - (segMidVecB));
                    float3 v2 = (point2 - (segMidVecA));
                    float3 v3 = (point3 - (segMidVecB));
                    float3 v4 = (point4 - (segMidVecA));



                    v1.Normalize();
                    v2.Normalize();
                    v3.Normalize();
                    v4.Normalize();

                    norms[4 * i * slices + 4 * j]     = v1;
                    norms[4 * i * slices + 4 * j + 1] = v2;
                    norms[4 * i * slices + 4 * j + 2] = v3;
                    norms[4 * i * slices + 4 * j + 3] = v4;
                }
            }

            return(new Mesh
            {
                Vertices = verts,
                Normals = norms,
                Triangles = tris,
            });
        }
Ejemplo n.º 12
0
        void Application_Idle(object sender, EventArgs e)
        {
            if (m_device == null)
            {
                return;
            }

            // Setup global data
            m_CB_Main.m.iResolution = new float2(panelOutput.Width, panelOutput.Height);
            m_CB_Main.m.tanHalfFOV  = Mathf.Tan(0.5f * Mathf.ToRad(FOV_DEGREES));
            m_CB_Main.m.iGlobalTime = GetGameTime() - m_startTime;
            m_CB_Main.UpdateData();

            // Setup light data
            float3 wsLightPosition = new float3(floatTrackbarControlLightPosX.Value, floatTrackbarControlLightPosY.Value, floatTrackbarControlLightPosZ.Value);
            float3 at = (m_wsLightTargetPosition - wsLightPosition).Normalized;

            if (radioButtonNegativeFreeTarget.Checked)
            {
                at = -at;
            }
            else if (radioButtonHorizontalTarget.Checked)
            {
                at.y = 0;
                at.Normalize();
            }

            float  roll = Mathf.ToRad(floatTrackbarControlLightRoll.Value);
            float3 left, up;

            at.OrthogonalBasis(out left, out up);

            float3 axisX = Mathf.Cos(roll) * left + Mathf.Sin(roll) * up;
            float3 axisY = -Mathf.Sin(roll) * left + Mathf.Cos(roll) * up;

            float radiusX = floatTrackbarControlLightScaleX.Value;
            float radiusY = floatTrackbarControlLightScaleY.Value;

            m_CB_Light.m._luminance = floatTrackbarControlLuminance.Value;
            m_CB_Light.m._wsLight2World.r0.Set(axisX, radiusX);
            m_CB_Light.m._wsLight2World.r1.Set(axisY, radiusY);
            m_CB_Light.m._wsLight2World.r2.Set(at, Mathf.PI * radiusX * radiusY);               // Disk area in W
            m_CB_Light.m._wsLight2World.r3.Set(wsLightPosition, 1);
            m_CB_Light.UpdateData();

            // Upload FGD & LTC tables
            m_tex_MSBRDF_E.SetPS(2);
            m_tex_MSBRDF_Eavg.SetPS(3);
            m_tex_LTC.SetPS(4);
            m_tex_MS_LTC.SetPS(5);

            // =========== Render scene ===========
            m_device.SetRenderStates(RASTERIZER_STATE.CULL_NONE, DEPTHSTENCIL_STATE.READ_WRITE_DEPTH_LESS, BLEND_STATE.DISABLED);

            if (!checkBoxShowDiff.Checked)
            {
                m_device.SetRenderTarget(m_device.DefaultTarget, m_device.DefaultDepthStencil);
                m_device.Clear(m_device.DefaultTarget, float4.Zero);
                m_device.ClearDepthStencil(m_device.DefaultDepthStencil, 1.0f, 0, true, false);

                if (checkBoxShowReference.Checked)
                {
                    // Use expensive reference
                    if (m_shader_RenderScene_Reference.Use())
                    {
                        m_device.RenderFullscreenQuad(m_shader_RenderScene_Reference);
                    }
                }
                else
                {
                    if (m_shader_RenderScene.Use())
                    {
                        m_device.RenderFullscreenQuad(m_shader_RenderScene);
                    }
                }
            }
            else
            {
                // Render reference in RT0
                m_device.SetRenderTarget(m_RT_temp0, m_device.DefaultDepthStencil);
                m_device.ClearDepthStencil(m_device.DefaultDepthStencil, 1.0f, 0, true, false);
                if (m_shader_RenderScene_Reference.Use())
                {
                    m_device.RenderFullscreenQuad(m_shader_RenderScene_Reference);
                }

                // Render LTC in RT1
                m_device.SetRenderTarget(m_RT_temp1, m_device.DefaultDepthStencil);
                m_device.ClearDepthStencil(m_device.DefaultDepthStencil, 1.0f, 0, true, false);
                if (m_shader_RenderScene.Use())
                {
                    m_device.RenderFullscreenQuad(m_shader_RenderScene);
                }

                // Render difference
                m_device.SetRenderStates(RASTERIZER_STATE.NOCHANGE, DEPTHSTENCIL_STATE.DISABLED, BLEND_STATE.DISABLED);
                m_device.SetRenderTarget(m_device.DefaultTarget, null);
                if (m_shader_RenderDiff.Use())
                {
                    m_RT_temp0.SetPS(0);
                    m_RT_temp1.SetPS(1);
                    m_tex_FalseColors.SetPS(2);

                    m_device.RenderFullscreenQuad(m_shader_RenderDiff);
                }
            }

            // =========== Render Light Disk ===========
            m_device.SetRenderTarget(m_device.DefaultTarget, m_device.DefaultDepthStencil);
            if (!checkBoxDebugMatrix.Checked)
            {
                m_device.SetRenderStates(RASTERIZER_STATE.CULL_NONE, DEPTHSTENCIL_STATE.READ_WRITE_DEPTH_LESS, BLEND_STATE.DISABLED);

                if (m_shader_RenderLight.Use())
                {
                    m_prim_disk.Render(m_shader_RenderLight);
                }
            }
            else
            {
                if (m_shader_RenderTestQuad.Use())
                {
                    m_CB_TestQuad.m._wsLight2World.r0.Set(radiusX * axisX, 0);
                    m_CB_TestQuad.m._wsLight2World.r1.Set(radiusY * axisY, 0);
                    m_CB_TestQuad.m._wsLight2World.r2.Set(at, 0);
                    m_CB_TestQuad.m._wsLight2World.r3.Set(wsLightPosition, 1);

                    // Upload the full matrix, although we only really need the 4 non trivial coefficients at indices m11, m13, m31 and m33...
                    int roughnessIndex = (int)Mathf.Floor(63.99f * Mathf.Sqrt(floatTrackbarControlRoughness.Value));
                    int thetaIndex     = (int)Mathf.Floor(63.99f * Mathf.Sqrt(1.0f - Mathf.Cos(Mathf.ToRad(floatTrackbarControlViewAngle.Value))));
                    int matrixIndex    = roughnessIndex + 64 * thetaIndex;

                    double[,]       LTC = radioButtonGGX.Checked ? LTCAreaLight.s_LtcMatrixData_GGX : LTCAreaLight.s_LtcMatrixData_OrenNayar;

                    // NOTE: The LTC inverse matrices stored in the tables are transposed: columns are stored first
                    // So in order to use them in the shaders, we need to compute P * M^-1^T instead of the paper's formulation M^-1 * P
                    //
                    m_CB_TestQuad.m._invM_transposed_r0.Set((float)LTC[matrixIndex, 0], (float)LTC[matrixIndex, 1], (float)LTC[matrixIndex, 2], 0);
                    m_CB_TestQuad.m._invM_transposed_r1.Set((float)LTC[matrixIndex, 3], (float)LTC[matrixIndex, 4], (float)LTC[matrixIndex, 5], 0);
                    m_CB_TestQuad.m._invM_transposed_r2.Set((float)LTC[matrixIndex, 6], (float)LTC[matrixIndex, 7], (float)LTC[matrixIndex, 8], 0);

                    m_CB_TestQuad.UpdateData();

                    m_device.RenderFullscreenQuad(m_shader_RenderTestQuad);
                }
            }

            // Show!
            m_device.Present(false);
        }