示例#1
0
 public void OnRenderTaskEnd(SceneRenderTask task, GPUContext context)
 {
     Return(0, Output);
     Return(1, RenderTask.Buffers?.DepthBuffer);
     Return(2, RenderTask.Buffers?.MotionVectors);
 }
示例#2
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            _preview.CubeTexture = (CubeTexture)request.Asset;
            _preview.Parent      = guiRoot;

            _preview.Task.Internal_Render(context);
        }
示例#3
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            _preview.CubeTexture = (CubeTexture)request.Asset;
            _preview.Parent      = guiRoot;
            _preview.SyncBackbufferSize();

            _preview.Task.OnDraw();
        }
示例#4
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            _preview.Parent = guiRoot;

            _preview.Task.OnRender(context);
        }
示例#5
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            var preview = (AudioClipPreview)request.Tag;

            preview.Parent = guiRoot;
        }
示例#6
0
 protected abstract void OnRenderUpdate(GPUContext context);
示例#7
0
        /// <summary>
        /// Called when thumbnail rendering begins.
        /// </summary>
        /// <param name="task">The scene rendering task to customize.</param>
        /// <param name="context">The GPU rendering context.</param>
        /// <param name="req">The request data.</param>
        public void OnThumbnailRenderingBegin(SceneRenderTask task, GPUContext context, ref CameraCutThumbnailRenderer.Request req)
        {
            RenderView view        = new RenderView();
            var        track       = (CameraCutTrack)Track;
            Camera     cam         = track.Camera;
            var        viewport    = new FlaxEngine.Viewport(Vector2.Zero, task.Buffers.Size);
            Quaternion orientation = Quaternion.Identity;

            view.Near = 10.0f;
            view.Far  = 20000.0f;
            bool  usePerspective    = true;
            float orthoScale        = 1.0f;
            float fov               = 60.0f;
            float customAspectRatio = 0.0f;

            view.RenderLayersMask = new LayersMask(uint.MaxValue);

            // Try to evaluate camera properties based on the initial camera state
            if (cam)
            {
                view.Position         = cam.Position;
                orientation           = cam.Orientation;
                view.Near             = cam.NearPlane;
                view.Far              = cam.FarPlane;
                usePerspective        = cam.UsePerspective;
                orthoScale            = cam.OrthographicScale;
                fov                   = cam.FieldOfView;
                customAspectRatio     = cam.CustomAspectRatio;
                view.RenderLayersMask = cam.RenderLayersMask;
            }

            // Try to evaluate camera properties based on the animated tracks
            float time = Start;

            if (req.ThumbnailIndex == 1)
            {
                time += Duration;
            }
            else if (req.ThumbnailIndex == 2)
            {
                time += Duration * 0.5f;
            }
            foreach (var subTrack in track.SubTracks)
            {
                if (subTrack is MemberTrack memberTrack)
                {
                    object value = memberTrack.Evaluate(time);
                    if (value != null)
                    {
                        // TODO: try to make it better
                        if (memberTrack.MemberName == "Position" && value is Vector3 asPosition)
                        {
                            view.Position = asPosition;
                        }

                        else if (memberTrack.MemberName == "Orientation" && value is Quaternion asRotation)
                        {
                            orientation = asRotation;
                        }

                        else if (memberTrack.MemberName == "NearPlane" && value is float asNearPlane)
                        {
                            view.Near = asNearPlane;
                        }

                        else if (memberTrack.MemberName == "FarPlane" && value is float asFarPlane)
                        {
                            view.Far = asFarPlane;
                        }

                        else if (memberTrack.MemberName == "UsePerspective" && value is bool asUsePerspective)
                        {
                            usePerspective = asUsePerspective;
                        }

                        else if (memberTrack.MemberName == "FieldOfView" && value is float asFieldOfView)
                        {
                            fov = asFieldOfView;
                        }

                        else if (memberTrack.MemberName == "CustomAspectRatio" && value is float asCustomAspectRatio)
                        {
                            customAspectRatio = asCustomAspectRatio;
                        }

                        else if (memberTrack.MemberName == "OrthographicScale" && value is float asOrthographicScale)
                        {
                            orthoScale = asOrthographicScale;
                        }
                    }
                }
            }

            // Build view
            view.Direction = Vector3.Forward * orientation;
            if (usePerspective)
            {
                float aspect = customAspectRatio <= 0.0f ? viewport.AspectRatio : customAspectRatio;
                view.Projection = Matrix.PerspectiveFov(fov * Mathf.DegreesToRadians, aspect, view.Near, view.Far);
            }
            else
            {
                view.Projection = Matrix.Ortho(viewport.Width * orthoScale, viewport.Height * orthoScale, view.Near, view.Far);
            }

            Vector3 target = view.Position + view.Direction;
            var     up     = Vector3.Transform(Vector3.Up, orientation);

            view.View = Matrix.LookAt(view.Position, target, up);
            view.NonJitteredProjection  = view.Projection;
            view.TemporalAAJitter       = Vector4.Zero;
            view.ModelLODDistanceFactor = 100.0f;
            view.Flags = ViewFlags.DefaultGame & ~(ViewFlags.MotionBlur);
            view.UpdateCachedData();
            task.View = view;
        }
 public override void RenderFrame(GPUContext context, ref StagingTexture frame, RenderOptions options, GPUTexture output)
 {
     // Copy texture back to the staging texture
     context.CopyTexture(frame.Texture, 0, 0, 0, 0, output, 0);
 }
示例#9
0
 /// <summary>
 /// Called when thumbnail drawing begins. Proxy should setup scene GUI for guiRoot.
 /// </summary>
 /// <param name="request">The request to render thumbnail.</param>
 /// <param name="guiRoot">The GUI root container control.</param>
 /// <param name="context">GPU context.</param>
 public virtual void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
 {
     guiRoot.AddChild(new Label
     {
         Text     = Name,
         Size     = guiRoot.Size,
         Wrapping = TextWrapping.WrapWords
     });
 }
 public abstract void RenderFrame(GPUContext context, ref StagingTexture frame, RenderOptions options, GPUTexture output);
 public abstract void CollectFrame(GPUContext context, ref StagingTexture frame, RenderOptions options);
示例#12
0
 /// <summary>
 /// Performs custom postFx rendering.
 /// </summary>
 /// <param name="context">The GPU commands context.</param>
 /// <param name="task">The current rendering task.</param>
 /// <param name="input">The input texture.</param>
 /// <param name="output">The output texture.</param>
 public abstract void Render(GPUContext context, SceneRenderTask task, RenderTarget input, RenderTarget output);
示例#13
0
 /// <summary>
 /// Performs custom postFx rendering.
 /// </summary>
 /// <param name="context">The GPU commands context.</param>
 /// <param name="renderContext">The rendering context.</param>
 /// <param name="input">The input texture.</param>
 /// <param name="output">The output texture.</param>
 public abstract void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output);
示例#14
0
 /// <inheritdoc />
 public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
 {
     guiRoot.AddChild(new Label
     {
         Text     = "Audio",
         Size     = guiRoot.Size,
         Wrapping = TextWrapping.WrapWords
     });
 }
示例#15
0
        /// <inheritdoc />
        public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output)
        {
            if (Viewport == null)
            {
                throw new NullReferenceException();
            }

            Profiler.BeginEventGPU("Editor Primitives");

            // Check if use MSAA
            var  format         = output.Format;
            var  formatFeatures = GPUDevice.Instance.GetFormatFeatures(format);
            bool enableMsaa     = formatFeatures.MSAALevelMax >= MSAALevel.X4 && Editor.Instance.Options.Options.Visual.EnableMSAAForDebugDraw;

            // Prepare
            var msaaLevel = enableMsaa ? MSAALevel.X4 : MSAALevel.None;
            var width     = output.Width;
            var height    = output.Height;
            var desc      = GPUTextureDescription.New2D(width, height, format, GPUTextureFlags.RenderTarget | GPUTextureFlags.ShaderResource, 1, 1, msaaLevel);
            var target    = RenderTargetPool.Get(ref desc);

            desc = GPUTextureDescription.New2D(width, height, PixelFormat.D24_UNorm_S8_UInt, GPUTextureFlags.DepthStencil, 1, 1, msaaLevel);
            var targetDepth = RenderTargetPool.Get(ref desc);

            // Copy frame and clear depth
            context.Draw(target, input);
            context.ClearDepth(targetDepth.View());
            context.SetViewport(width, height);
            context.SetRenderTarget(targetDepth.View(), target.View());

            // Draw gizmos and other editor primitives
            var renderList = RenderList.GetFromPool();
            var prevList   = renderContext.List;

            renderContext.List = renderList;
            for (int i = 0; i < Viewport.Gizmos.Count; i++)
            {
                Viewport.Gizmos[i].Draw(ref renderContext);
            }
            Viewport.DrawEditorPrimitives(context, ref renderContext, target, targetDepth);

            // Sort draw calls
            renderList.SortDrawCalls(ref renderContext, false, DrawCallsListType.GBuffer);
            renderList.SortDrawCalls(ref renderContext, false, DrawCallsListType.GBufferNoDecals);
            renderList.SortDrawCalls(ref renderContext, true, DrawCallsListType.Forward);

            // Perform the rendering
            renderContext.View.Pass = DrawPass.GBuffer;
            renderList.ExecuteDrawCalls(ref renderContext, DrawCallsListType.GBuffer);
            renderList.ExecuteDrawCalls(ref renderContext, DrawCallsListType.GBufferNoDecals);
            renderContext.View.Pass = DrawPass.Forward;
            renderList.ExecuteDrawCalls(ref renderContext, DrawCallsListType.Forward);

            // Resolve MSAA texture
            if (enableMsaa)
            {
                context.ResolveMultisample(target, output);
            }
            else
            {
                context.Draw(output, target);
            }

            // Cleanup
            RenderTargetPool.Release(targetDepth);
            RenderTargetPool.Release(target);
            RenderList.ReturnToPool(renderList);
            renderContext.List = prevList;

            Profiler.EndEventGPU();
        }
示例#16
0
        private void OnRender(GPUContext context)
        {
            lock (_requests)
            {
                // Check if there is ready next asset to render thumbnail for it
                // But don't check whole queue, only a few items
                var request = GetReadyRequest(10);
                if (request == null)
                {
                    // Disable task
                    _task.Enabled = false;
                    return;
                }

                // Find atlas with an free slot
                var atlas = GetValidAtlas();
                if (atlas == null)
                {
                    // Error
                    _task.Enabled = false;
                    _requests.Clear();
                    Editor.LogError("Failed to get atlas.");
                    return;
                }

                // Wait for atlas being loaded
                if (!atlas.IsReady)
                {
                    return;
                }

                // Setup
                _guiRoot.RemoveChildren();
                _guiRoot.AccentColor = request.Proxy.AccentColor;

                // Call proxy to prepare for thumbnail rendering
                request.Proxy.OnThumbnailDrawBegin(request, _guiRoot, context);
                _guiRoot.UnlockChildrenRecursive();

                // Draw preview
                context.Clear(_output, Color.Black);
                Render2D.CallDrawing(_guiRoot, context, _output);

                // Call proxy and cleanup UI (delete create controls, shared controls should be unlinked during OnThumbnailDrawEnd event)
                request.Proxy.OnThumbnailDrawEnd(request, _guiRoot);
                _guiRoot.DisposeChildren();

                // Copy backbuffer with rendered preview into atlas
                Sprite icon = atlas.OccupySlot(_output, request.Item.ID);
                if (!icon.IsValid)
                {
                    // Error
                    _task.Enabled = false;
                    _requests.Clear();
                    Editor.LogError("Failed to occupy previews cache atlas slot.");
                    return;
                }

                // End
                request.FinishRender(ref icon);
                RemoveRequest(request);
            }
        }
        /// <summary>
        /// Called when thumbnail rendering begins.
        /// </summary>
        /// <param name="task">The scene rendering task to customize.</param>
        /// <param name="context">The GPU rendering context.</param>
        /// <param name="req">The request data.</param>
        public void OnThumbnailRenderingBegin(SceneRenderTask task, GPUContext context, ref CameraCutThumbnailRenderer.Request req)
        {
            var view        = new RenderView();
            var track       = (CameraCutTrack)Track;
            var cam         = track.Camera;
            var viewport    = new FlaxEngine.Viewport(Vector2.Zero, task.Buffers.Size);
            var orientation = Quaternion.Identity;

            view.Near = 10.0f;
            view.Far  = 20000.0f;
            var usePerspective    = true;
            var orthoScale        = 1.0f;
            var fov               = 60.0f;
            var customAspectRatio = 0.0f;

            // Try to evaluate camera properties based on the initial camera state
            if (cam)
            {
                view.Position     = cam.Position;
                orientation       = cam.Orientation;
                view.Near         = cam.NearPlane;
                view.Far          = cam.FarPlane;
                usePerspective    = cam.UsePerspective;
                orthoScale        = cam.OrthographicScale;
                fov               = cam.FieldOfView;
                customAspectRatio = cam.CustomAspectRatio;
            }

            // Try to evaluate camera properties based on the animated tracks
            var time = req.ThumbnailIndex == 0 ? Start : Start + Duration;

            foreach (var subTrack in track.SubTracks)
            {
                if (subTrack is MemberTrack memberTrack)
                {
                    object value = memberTrack.Evaluate(time);
                    if (value != null)
                    {
                        // TODO: try to make it better
                        if (memberTrack.MemberName == "Position" && value is Vector3 asPosition)
                        {
                            view.Position = asPosition;
                        }
                        else if (memberTrack.MemberName == "Orientation" && value is Quaternion asRotation)
                        {
                            orientation = asRotation;
                        }
                        else if (memberTrack.MemberName == "NearPlane" && value is float asNearPlane)
                        {
                            view.Near = asNearPlane;
                        }
                        else if (memberTrack.MemberName == "FarPlane" && value is float asFarPlane)
                        {
                            view.Far = asFarPlane;
                        }
                        else if (memberTrack.MemberName == "UsePerspective" && value is bool asUsePerspective)
                        {
                            usePerspective = asUsePerspective;
                        }
                        else if (memberTrack.MemberName == "FieldOfView" && value is float asFieldOfView)
                        {
                            fov = asFieldOfView;
                        }
                        else if (memberTrack.MemberName == "CustomAspectRatio" && value is float asCustomAspectRatio)
                        {
                            customAspectRatio = asCustomAspectRatio;
                        }
                        else if (memberTrack.MemberName == "OrthographicScale" && value is float asOrthographicScale)
                        {
                            orthoScale = asOrthographicScale;
                        }
                    }
                }
            }

            // Build view
            view.Direction = Vector3.Forward * orientation;
            if (usePerspective)
            {
                float aspect = customAspectRatio <= 0.0f ? viewport.AspectRatio : customAspectRatio;
                view.Projection = Matrix.PerspectiveFov(fov * Mathf.DegreesToRadians, aspect, view.Near, view.Far);
            }
            else
            {
                view.Projection = Matrix.Ortho(viewport.Width * orthoScale, viewport.Height * orthoScale, view.Near, view.Far);
            }
            Vector3 target = view.Position + view.Direction;
            var     up     = Vector3.Transform(Vector3.Up, orientation);

            view.View = Matrix.LookAt(view.Position, target, up);
            view.NonJitteredProjection  = view.Projection;
            view.TemporalAAJitter       = Vector4.Zero;
            view.ModelLODDistanceFactor = 100.0f;
            view.Flags = ViewFlags.Reflections | ViewFlags.SSR | ViewFlags.AO | ViewFlags.GI |
                         ViewFlags.DirectionalLights | ViewFlags.PointLights | ViewFlags.SpotLights | ViewFlags.SkyLights |
                         ViewFlags.Shadows | ViewFlags.SpecularLight | ViewFlags.AntiAliasing | ViewFlags.CustomPostProcess |
                         ViewFlags.Bloom | ViewFlags.ToneMapping | ViewFlags.CameraArtifacts | ViewFlags.LensFlares | ViewFlags.Decals |
                         ViewFlags.DepthOfField | ViewFlags.Fog;
            view.UpdateCachedData();
            task.View = view;
        }
示例#18
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            var asset = FlaxEngine.Content.LoadAsync <FontAsset>(request.Item.ID);

            guiRoot.AddChild(new Label
            {
                Text         = asset.FamilyName,
                AnchorPreset = AnchorPresets.StretchAll,
                Offsets      = Margin.Zero,
                Wrapping     = TextWrapping.WrapWords
            });
        }
示例#19
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            var asset = FlaxEngine.Content.Load <FontAsset>(request.Item.Path);

            guiRoot.AddChild(new Label(Vector2.Zero, guiRoot.Size)
            {
                Text     = asset.FamilyName,
                Wrapping = TextWrapping.WrapWords
            });
        }
 public void OnRenderTaskEnd(SceneRenderTask task, GPUContext context)
 {
     Return(0, Output);
 }
示例#21
0
        private void OnEnd(RenderTask task, GPUContext context)
        {
            // Pick the atlas or create a new one
            int atlasIndex = -1;

            for (int i = 0; i < _atlases.Count; i++)
            {
                if (!_atlases[i].IsFull)
                {
                    atlasIndex = i;
                    break;
                }
            }
            if (atlasIndex == -1)
            {
                // Setup configuration
                var atlasSize   = 1024;
                var atlasFormat = PixelFormat.R8G8B8A8_UNorm;
                var width       = (float)Width;
                var height      = (float)Height;
                var countX      = Mathf.FloorToInt(atlasSize / width);
                var countY      = Mathf.FloorToInt(atlasSize / height);
                var count       = countX * countY;

                // Create sprite atlas texture
                var spriteAtlas = FlaxEngine.Content.CreateVirtualAsset <SpriteAtlas>();
                var data        = new byte[atlasSize * atlasSize * PixelFormatExtensions.SizeInBytes(atlasFormat)];
                var initData    = new TextureBase.InitData
                {
                    Width     = atlasSize,
                    Height    = atlasSize,
                    ArraySize = 1,
                    Format    = atlasFormat,
                    Mips      = new[]
                    {
                        new TextureBase.InitData.MipData
                        {
                            Data       = data,
                            RowPitch   = data.Length / atlasSize,
                            SlicePitch = data.Length
                        },
                    },
                };
                spriteAtlas.Init(ref initData);

                // Setup sprite atlas slots (each per thumbnail)
                var thumbnailSizeUV = new Vector2(width / atlasSize, height / atlasSize);
                for (int i = 0; i < count; i++)
                {
                    var x = i % countX;
                    var y = i / countX;
                    var s = new Sprite
                    {
                        Name = string.Empty,
                        Area = new Rectangle(new Vector2(x, y) * thumbnailSizeUV, thumbnailSizeUV),
                    };
                    spriteAtlas.AddSprite(s);
                }

                // Add atlas to the cached ones
                atlasIndex = _atlases.Count;
                _atlases.Add(new Atlas
                {
                    Texture    = spriteAtlas,
                    SlotsUsage = new BitArray(count, false),
                    Count      = 0,
                });
            }

            // Skip ending if the atlas is not loaded yet (streaming backend uploads texture to GPU or sth)
            var atlas = _atlases[atlasIndex];

            if (atlas.Texture.ResidentMipLevels == 0)
            {
                return;
            }

            // Pick the sprite slot from the atlas
            var spriteIndex = -1;

            for (int i = 0; i < atlas.SlotsUsage.Count; i++)
            {
                if (atlas.SlotsUsage[i] == false)
                {
                    atlas.SlotsUsage[i] = true;
                    spriteIndex         = i;
                    break;
                }
            }
            if (spriteIndex == -1)
            {
                throw new FlaxException();
            }
            atlas.Count++;
            _atlases[atlasIndex] = atlas;
            var sprite = new SpriteHandle(atlas.Texture, spriteIndex);

            // Copy output frame to the sprite atlas slot
            var spriteLocation = sprite.Location;

            context.CopyTexture(atlas.Texture.Texture, 0, (uint)spriteLocation.X, (uint)spriteLocation.Y, 0, _output, 0);

            // Link sprite to the UI
            var req = _queue[0];

            req.Media.OnThumbnailRenderingEnd((SceneRenderTask)task, context, ref req, ref sprite);

            // End
            _queue.RemoveAt(0);
            task.Enabled = false;
        }
示例#22
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            _preview.Prefab = (Prefab)request.Asset;
            _preview.Parent = guiRoot;
            _preview.Scale  = Vector2.One;
            _preview.ShowDefaultSceneActors = true;
            _preview.SyncBackbufferSize();

            // Special case for UI prefabs
            if (_preview.Instance is UIControl uiControl && uiControl.HasControl)
            {
                // Ensure to place UI in a proper way
                uiControl.Control.Location     = Vector2.Zero;
                uiControl.Control.Scale       *= PreviewsCache.AssetIconSize / uiControl.Control.Size.MaxValue;
                uiControl.Control.AnchorPreset = AnchorPresets.TopLeft;
                uiControl.Control.AnchorPreset = AnchorPresets.MiddleCenter;

                // Tweak preview
                _preview.ShowDefaultSceneActors = false;
            }
示例#23
0
 /// <inheritdoc />
 public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
 {
     guiRoot.AddChild(new Label
     {
         Text     = Path.GetFileNameWithoutExtension(request.Asset.Path),
         Size     = guiRoot.Size,
         Wrapping = TextWrapping.WrapWords
     });
 }
示例#24
0
 /// <summary>
 /// Performs custom postFx rendering.
 /// </summary>
 /// <param name="context">The GPU commands context.</param>
 /// <param name="task">The current rendering task.</param>
 /// <param name="input">The input texture.</param>
 /// <param name="output">The output texture.</param>
 public abstract void Render(GPUContext context, SceneRenderTask task, GPUTexture input, GPUTexture output);
示例#25
0
 /// <summary>
 /// Called when thumbnail drawing begins. Proxy should setup scene GUI for guiRoot.
 /// </summary>
 /// <param name="request">The request to render thumbnail.</param>
 /// <param name="guiRoot">The GUI root container control.</param>
 /// <param name="context">GPU context.</param>
 public virtual void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
 {
     guiRoot.AddChild(new Label
     {
         Text         = Name,
         Offsets      = Margin.Zero,
         AnchorPreset = AnchorPresets.StretchAll,
         Wrapping     = TextWrapping.WrapWords
     });
 }
示例#26
0
 /// <inheritdoc />
 public void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth)
 {
 }
 protected override void OnRenderUpdate(GPUContext context)
 {
     Return(0, GetInput(0));
 }
 /// <inheritdoc />
 public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
 {
     _preview.Asset  = (SpriteAtlas)request.Asset;
     _preview.Parent = guiRoot;
 }
示例#29
0
        /// <inheritdoc />
        public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
        {
            _preview.Material = (MaterialInstance)request.Asset;
            _preview.Parent   = guiRoot;

            _preview.Task.Internal_Render(context);
        }
示例#30
0
 public void OnRenderTaskBegin(SceneRenderTask task, GPUContext context)
 {
     Output.Size = Size;
 }