public static void UpdateParameters(RenderContext context, CameraComponent camera)
        {
            if (camera == null) throw new ArgumentNullException("camera");

            // Setup viewport size
            var currentViewport = context.GraphicsDevice.Viewport;
            var aspectRatio = currentViewport.AspectRatio;

            // Update the aspect ratio
            if (camera.UseCustomAspectRatio)
            {
                aspectRatio = camera.AspectRatio;
            }

            // If the aspect ratio is calculated automatically from the current viewport, update matrices here
            camera.Update(aspectRatio);

            // Store the current view/projection matrix in the context
            var viewParameters = context.Parameters;
            viewParameters.Set(TransformationKeys.View, camera.ViewMatrix);
            viewParameters.Set(TransformationKeys.Projection, camera.ProjectionMatrix);
            viewParameters.Set(TransformationKeys.ViewProjection, camera.ViewProjectionMatrix);
            viewParameters.Set(CameraKeys.NearClipPlane, camera.NearClipPlane);
            viewParameters.Set(CameraKeys.FarClipPlane, camera.FarClipPlane);
            viewParameters.Set(CameraKeys.VerticalFieldOfView, camera.VerticalFieldOfView);
            viewParameters.Set(CameraKeys.OrthoSize, camera.OrthographicSize);
            viewParameters.Set(CameraKeys.ViewSize, new Vector2(currentViewport.Width, currentViewport.Height));
            viewParameters.Set(CameraKeys.AspectRatio, aspectRatio);

            //viewParameters.Set(CameraKeys.FocusDistance, camera.FocusDistance);
        }
Exemple #2
0
        protected override float ComputeScreenCoverage(CameraComponent camera, Vector3 position, Vector3 direction, float width, float height)
        {
            // http://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space
            var targetPosition = new Vector4(position, 1.0f);
            Vector4 projectedTarget;
            Vector4.Transform(ref targetPosition, ref camera.ViewProjectionMatrix, out projectedTarget);

            var d = Math.Abs(projectedTarget.W) + 0.00001f;
            var r = Radius;
            var coTanFovBy2 = camera.ProjectionMatrix.M22;
            var pr = r * coTanFovBy2 / (Math.Sqrt(d * d - r * r) + 0.00001f);

            // Size on screen
            return (float)pr * Math.Max(width, height);
        }
        protected override void DrawCore(RenderContext context)
        {
            modelProcessor = SceneInstance.GetCurrent(context).GetProcessor<ModelProcessor>();
            lightProcessor = SceneInstance.GetCurrent(context).GetProcessor<LightProcessor>();

            // No light processors means no light in the scene, so we can early exit
            if (lightProcessor == null || modelProcessor == null)
            {
                return;
            }

            // Not in the context of a SceneCameraRenderer? just exit
            sceneCameraRenderer = context.Tags.Get(SceneCameraRenderer.Current);
            sceneCamera = context.Tags.Get(CameraComponentRenderer.Current);
            if (sceneCameraRenderer == null || sceneCamera == null)
            {
                return;
            }
            sceneCullingMask = sceneCameraRenderer.CullingMask;

            // Setup the callback on the ModelRenderer and shadow map LightGroupRenderer
            if (!isModelComponentRendererSetup)
            {
                // TODO: Check if we could discover declared renderers in a better way than just hacking the tags of a component
                var modelRenderer = ModelComponentRenderer.GetAttached(sceneCameraRenderer);
                if (modelRenderer == null)
                {
                    return;
                }

                modelRenderer.Callbacks.PreRenderModel += PrepareRenderModelForRendering;
                modelRenderer.Callbacks.PreRenderMesh += PreRenderMesh;

                // TODO: Make this pluggable
                // TODO: Shadows should work on mobile platforms
                if (context.GraphicsDevice.Features.Profile >= GraphicsProfile.Level_10_0
                    && (Platform.Type == PlatformType.Windows || Platform.Type == PlatformType.WindowsStore || Platform.Type == PlatformType.Windows10))
                {
                    shadowMapRenderer = new ShadowMapRenderer(modelRenderer.EffectName);
                    shadowMapRenderer.Renderers.Add(typeof(LightDirectional), new LightDirectionalShadowMapRenderer());
                    shadowMapRenderer.Renderers.Add(typeof(LightSpot), new LightSpotShadowMapRenderer());
                }

                isModelComponentRendererSetup = true;
            }

            // Collect all visible lights
            CollectVisibleLights();

            // Draw shadow maps
            if (shadowMapRenderer != null)
                shadowMapRenderer.Draw(context, visibleLightsWithShadows);

            // Prepare active renderers in an ordered list (by type and shadow on/off)
            CollectActiveLightRenderers(context);

            currentModelLightShadersPermutationEntry = null;
            currentModelShadersParameters = null;
            currentShadowReceiver = true;

            // Clear the cache of parameter entries
            lightParameterEntries.Clear();
            parameterCollectionEntryPool.Clear();

            // Clear association between model and lights
            modelToLights.Clear();

            // Clear all data generated by shader entries
            foreach (var shaderEntry in shaderEntries)
            {
                shaderEntry.Value.ResetGroupDatas();
            }
        }
Exemple #4
0
        /// <summary>
        /// Calculate camera view parameters based on the entity bounding box.
        /// </summary>
        /// <param name="entity">The entity that we want to display</param>
        /// <param name="up">The vector representing the up of the entity</param>
        /// <param name="front">The vector representing the front of the entity</param>
        /// <param name="cameraComponent">The camera component used to render to object</param>
        /// <returns>Appropriate view parameters that can be used to display the entity</returns>
        public static ViewParameters CalculateViewParameters(Entity entity, Vector3 up, Vector3 front, CameraComponent cameraComponent)
        {
            var upAxis = up.Length() < MathUtil.ZeroTolerance ? Vector3.UnitY : Vector3.Normalize(up);

            // if we ensure that the whole object bounding box seen under the view vector in the frustum,
            // most of the time it results in object occupying only ~ 1/4 * 1/4 of the screen space.
            // So instead we decided to approximate the object with a bounding sphere (taking the risk that some of the objects are a little bit out of the screen)
            var boundingSphere = CalculateBoundingSpere(entity);

            // calculate the min distance from the object to see it entirely
            var minimunDistance = Math.Max(
                boundingSphere.Radius / (2f * (float)Math.Tan(cameraComponent.VerticalFieldOfView * cameraComponent.AspectRatio / 2f)),
                boundingSphere.Radius / (2f * (float)Math.Tan(cameraComponent.VerticalFieldOfView / 2f)));

            var distance = 1.2f * (minimunDistance + boundingSphere.Radius); // set the view distance such that the object can be seen entirely 
            var parameters = new ViewParameters(upAxis)
            {
                Target = boundingSphere.Center + entity.Transform.Position, // use of center of the bounding box as camera target
                Distance = distance,
                FarPlane = distance
            };

            return parameters;
        }
        private void UpdateFrustum(CameraComponent camera)
        {
            var projectionToView = camera.ProjectionMatrix;
            projectionToView.Invert();

            // Compute frustum-dependent variables (common for all shadow maps)
            var projectionToWorld = camera.ViewProjectionMatrix;
            projectionToWorld.Invert();

            // Transform Frustum corners in World Space (8 points) - algorithm is valid only if the view matrix does not do any kind of scale/shear transformation
            for (int i = 0; i < 8; ++i)
            {
                Vector3.TransformCoordinate(ref FrustumBasePoints[i], ref projectionToWorld, out frustumCornersWS[i]);
                Vector3.TransformCoordinate(ref FrustumBasePoints[i], ref projectionToView, out frustumCornersVS[i]);
            }
        }
Exemple #6
0
        protected override float ComputeScreenCoverage(CameraComponent camera, Vector3 position, Vector3 direction, float width, float height)
        {
            // http://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space
            // Use a sphere at target point to compute the screen coverage. This is a very rough approximation.
            // We compute the sphere at target point where the size of light is the largest
            // TODO: Check if we can improve this calculation with a better model
            var targetPosition = new Vector4(position + direction * Range, 1.0f);
            Vector4 projectedTarget;
            Vector4.Transform(ref targetPosition, ref camera.ViewProjectionMatrix, out projectedTarget);

            var d = Math.Abs(projectedTarget.W) + 0.00001f;
            var r = Range * Math.Sin(MathUtil.DegreesToRadians(AngleOuter/2.0f));
            var coTanFovBy2 = camera.ProjectionMatrix.M22;
            var pr = r * coTanFovBy2 / (Math.Sqrt(d * d - r * r) + 0.00001f);

            // Size on screen
            return (float)pr * Math.Max(width, height);
        }
 protected override float ComputeScreenCoverage(CameraComponent camera, Vector3 position, Vector3 direction, float width, float height)
 {
     // As the directional light is covering the whole screen, we take the max of current width, height
     return Math.Max(width, height);
 }
Exemple #8
0
        public async Task AutoswitchCamera(EngineContext engineContext)
        {
            bool autoswitch = false;
            while (true)
            {
                // Fetch camera list
                var cameras = Context.EntityManager.Entities
                    .Where(x => x.ContainsKey(CameraComponent.Key))
                    .Select(x => x.Get(CameraComponent.Key)).ToArray();

                if (engineContext.InputManager.IsKeyPressed(Keys.F8))
                {
                    autoswitch = !autoswitch;
                }

                int index = Array.IndexOf(cameras, TrackingCamera);
                if (autoswitch)
                {
                    if (index == 1)
                        index = 2;
                    else
                        index = 1;
                    TrackingCamera = (index < cameras.Length) ? cameras[index] : null;
                }

                await TaskEx.Delay((index == 1) ? 50000 : 10000);
            }
        }
Exemple #9
0
        public override async Task Execute()
        {
            var config = AppConfig.GetConfiguration<Config>("CameraScript");
            flyingAround = config.FlyingAround;
            
            //uiControl = Context.RenderContext.UIControl;

            if (camera == null)
            {
                // Near plane and Far plane are swapped in order to increase float precision for far distance as near distance is already having 
                // lots of precisions by the 1/z perspective projection
                //camera = new Camera(new R32G32B32_Float(200, 0, 200), new R32G32B32_Float(0, 0, 200), 1.2f, 1280.0f / 720.0f, 4000000.0f, 10.0f);
                var zNear = 10.0f;
                var zFar = 4000000.0f;
                if (Context.RenderContext.IsZReverse)
                {
                    var temp = zNear;
                    zNear = zFar;
                    zFar = temp;
                }
                camera = new Camera(new Vector3(200, 0, 200), new Vector3(0, 0, 200), 1.2f, RenderContext.Width, RenderContext.Height, (float)RenderContext.Width / RenderContext.Height, zNear, zFar);
                camera.Mode = CameraMode.Free;
            }

            Speed = config.Speed;

            //useful when we need to debug something from a specific point of view (we can first get the WorldToCamera value using a breakpoint then set it below)
            /*Matrix mat = new Matrix();
            mat.M11 = 0.0267117824f;
            mat.M12 = -0.9257705f;
            mat.M13 = -0.377141267f;
            mat.M14 = 0.0f;
            mat.M21 = -0.9996432f;
            mat.M22 = -0.024737807f;
            mat.M23 = -0.0100777093f;
            mat.M24 = 0.0f;
            mat.M31 = 0.0f;
            mat.M32 = 0.377275884f;
            mat.M33 = -0.926100969f;
            mat.M34 = 0.0f;
            mat.M41 = 531.524963f;
            mat.M42 = 501.754761f;
            mat.M43 = 989.462646f;
            mat.M44 = 1.0f;
            camera.WorldToCamera = mat;*/

            //uiControl.MouseWheel += OnUiControlOnMouseWheel;
            var st = new Stopwatch();

            st.Start();

            var viewParameters = Context.RenderContext.RenderPassPlugins.OfType<MainPlugin>().FirstOrDefault().ViewParameters;

            Context.Scheduler.Add(() => AutoswitchCamera(Context));

            var lastTime = DateTime.UtcNow;
            var pauseTime = false;
            while (true)
            {
                await Scheduler.Current.NextFrame();

                var mousePosition = Context.InputManager.MousePosition;

                if (Context.InputManager.IsMouseButtonPressed(MouseButton.Right))
                {
                    camera.Mode = CameraMode.Free;
                    isModifyingPosition = true;

                    pitch = 0;
                    yaw = 0;
                    roll = 0;

                    if (camera.Mode == CameraMode.Free)
                    {
                        fixedFreeMatrix = camera.WorldToCamera;
                    }
                    else
                    {
                        fixedPosition = camera.Position;
                    }
                }
                if (Context.InputManager.IsMouseButtonDown(MouseButton.Right))
                {
                    var deltaX = mousePosition.X - previousX;
                    var deltaY = mousePosition.Y - previousY;
                    if (isModifyingPosition)
                    {

                        yaw += deltaX * Speed / 1000.0f;
                        pitch += deltaY * Speed / 1000.0f;

                        if (camera.Mode == CameraMode.Target)
                        {
                            camera.Position = (Vector3)Vector3.Transform(fixedPosition, Matrix.RotationX(pitch) * Matrix.RotationZ(yaw));
                            Console.WriteLine(camera.Position);
                        }
                        else
                        {
                            camera.WorldToCamera = Camera.YawPitchRoll(camera.Position, fixedFreeMatrix, yaw, -pitch, roll);
                        }

                        //uiControl.Text = "" + camera.Mode;

                        viewChanged = true;
                    }
                    else if (isModifyingFov)
                    {
                        camera.FieldOfView += deltaX / 128.0f;
                        camera.FieldOfView = Math.Max(Math.Min(camera.FieldOfView, (float)Math.PI * 0.9f), 0.01f);

                        viewChanged = true;
                    }
                }
                if (Context.InputManager.IsMouseButtonReleased(MouseButton.Right))
                {
                    isModifyingFov = false;
                    isModifyingPosition = false;

                    if (camera.Mode == CameraMode.Free && flyingAround)
                    {
                        camera.Mode = CameraMode.Target;
                    }
                }

                previousX = mousePosition.X;
                previousY = mousePosition.Y;

                if (Context.InputManager.IsKeyDown(Keys.LeftAlt) && Context.InputManager.IsKeyPressed(Keys.Enter))
                    Context.RenderContext.GraphicsDevice.IsFullScreen = !Context.RenderContext.GraphicsDevice.IsFullScreen;

                if (Context.InputManager.IsKeyPressed(Keys.F2))
                    pauseTime = !pauseTime;

                var currentTime = DateTime.UtcNow;
                if (!pauseTime)
                {
                    Context.CurrentTime += currentTime - lastTime;
                    viewParameters.Set(GlobalKeys.Time, (float)Context.CurrentTime.TotalSeconds);
                    var timeStep = (float)(currentTime - lastTime).TotalMilliseconds;
                    viewParameters.Set(GlobalKeys.TimeStep, timeStep);
                }

                // Set the pause variable for the rendering
                Context.RenderContext.IsPaused = pauseTime;
                lastTime = currentTime;
               
                if (Context.InputManager.IsKeyPressed(Keys.Space))
                {
                    // Fetch camera list
                    var cameras = Context.EntityManager.Entities
                        .Where(x => x.ContainsKey(CameraComponent.Key))
                        .Select(x => x.Get(CameraComponent.Key)).ToArray();

                    // Go to next camera
                    // If no camera or unset, index will be 0, so it will go to first one
                    int index = Array.IndexOf(cameras, TrackingCamera) + 1;
                    TrackingCamera = (index < cameras.Length) ? cameras[index] : null;
                    clock.Restart();

                    //flyingAround = !flyingAround;
                    //if (flyingAround)
                    //{
                    //    fixedPosition = camera.Position;
                    //    clock.Restart();
                    //}
                }

                if (TrackingCamera != null)
                {
                    Matrix projection, worldToCamera;

                    // Overwrite near/far plane with our camera, as they are not reliable
                    TrackingCamera.NearPlane = camera.NearClipPlane;
                    TrackingCamera.FarPlane = camera.FarClipPlane;

                    TrackingCamera.Calculate(out projection, out worldToCamera);

                    viewParameters.Set(TransformationKeys.View, worldToCamera);
                    viewParameters.Set(TransformationKeys.Projection, projection);

                    // TODO: tracking camera doesn't have proper near/far plane values. Use mouse camera near/far instead.
                    viewParameters.Set(CameraKeys.NearClipPlane, camera.NearClipPlane);
                    viewParameters.Set(CameraKeys.FarClipPlane, camera.FarClipPlane);
                    viewParameters.Set(CameraKeys.FieldOfView, TrackingCamera.VerticalFieldOfView);
                    // Console.WriteLine("FOV:{0}", trackingCamera.VerticalFieldOfView);
                    viewParameters.Set(CameraKeys.ViewSize, new Vector2(camera.Width, camera.Height));
                    viewParameters.Set(CameraKeys.Aspect, TrackingCamera.AspectRatio);
                    viewParameters.Set(CameraKeys.FocusDistance, TrackingCamera.FocusDistance);
                }
                else
                {
                    bool moved = false;

                    var localPosition = new Vector3(0, 0, 0);

                    if (Context.InputManager.IsKeyDown(Keys.Left) || Context.InputManager.IsKeyDown(Keys.A))
                    {
                        localPosition.X -= 1.0f;
                        moved = true;
                    }

                    if (Context.InputManager.IsKeyDown(Keys.Right) || Context.InputManager.IsKeyDown(Keys.D))
                    {
                        localPosition.X += 1.0f;
                        moved = true;
                    }

                    if (Context.InputManager.IsKeyDown(Keys.Up) || Context.InputManager.IsKeyDown(Keys.W))
                    {
                        localPosition.Y -= 1.0f;
                        moved = true;
                    }
                    if (Context.InputManager.IsKeyDown(Keys.Down) || Context.InputManager.IsKeyDown(Keys.S))
                    {
                        localPosition.Y += 1.0f;
                        moved = true;
                    }

                    if (Context.InputManager.IsKeyDown(Keys.R))
                    {
                        roll += 0.1f;
                    }

                    if (Context.InputManager.IsKeyDown(Keys.T))
                    {
                        roll -= 0.1f;
                    }

                    var moveSpeedFactor = Context.InputManager.IsKeyDown(Keys.LeftShift) || Context.InputManager.IsKeyDown(Keys.RightShift) ? 0.1f : 1.0f;

                    localPosition.Normalize();
                    localPosition *= (float)clock.ElapsedTicks * 1000.0f * MoveSpeed * moveSpeedFactor / Stopwatch.Frequency;

                    localVelocity = localVelocity * 0.9f + localPosition * 0.1f;

                    if (localVelocity.Length() > MoveSpeed * 0.001f)
                    {

                        if (camera.Mode == CameraMode.Target)
                            camera.Position = camera.Position + localVelocity;
                        else
                        {
                            var destVector = ((Vector3)camera.WorldToCamera.Column3);
                            var leftRightVector = Vector3.Cross(destVector, Vector3.UnitZ);
                            leftRightVector.Normalize();

                            var newPosition = camera.Position - destVector * localVelocity.Y;
                            newPosition = newPosition - leftRightVector * localVelocity.X;
                            camera.Position = newPosition;
                        }

                        viewChanged = true;
                    }

                    if (flyingAround)
                    {
                        camera.Position = (Vector3)Vector3.Transform(camera.Position, Matrix.RotationZ(clock.ElapsedMilliseconds * Speed / 10000.0f));
                        viewChanged = true;
                    }

                    if (Context.InputManager.IsKeyPressed(Keys.F))
                    {
                        camera.FieldOfView -= 0.1f;
                        viewChanged = true;
                    }

                    if (Context.InputManager.IsKeyPressed(Keys.G))
                    {
                        camera.FieldOfView += 0.1f;
                        viewChanged = true;
                    }

                    //if (viewChanged)
                    {
                        viewParameters.Set(TransformationKeys.View, camera.WorldToCamera);
                        viewParameters.Set(TransformationKeys.Projection, camera.Projection);
                        viewParameters.Set(CameraKeys.NearClipPlane, camera.NearClipPlane);
                        viewParameters.Set(CameraKeys.FarClipPlane, camera.FarClipPlane);
                        viewParameters.Set(CameraKeys.FieldOfView, camera.FieldOfView);
                        //Console.WriteLine("FOV:{0}", camera.FieldOfView);
                        viewParameters.Set(CameraKeys.ViewSize, new Vector2(camera.Width, camera.Height));
                        viewParameters.Set(CameraKeys.Aspect, camera.Aspect);
                        viewParameters.Set(CameraKeys.FocusDistance, 0.0f);
                        //Console.WriteLine("Camera: {0}", camera);

                    }

                    if (Context.InputManager.IsKeyPressed(Keys.I))
                    {
                        var worldToCamera = camera.WorldToCamera;
                        var target = (Vector3)worldToCamera.Column3;
                        Console.WriteLine("camera.Position = new R32G32B32_Float({0}f,{1}f,{2}f); camera.Target = camera.Position + new R32G32B32_Float({3}f, {4}f, {5}f);", camera.Position.X, camera.Position.Y, camera.Position.Z, target.X, target.Y, target.Z);
                    }

                    viewChanged = false;
                    clock.Restart();
                }
            }
        }
        protected override async Task LoadContent()
        {
            CreatePipeline();

            await base.LoadContent();

            IsMouseVisible = true;
            rotateLights = true;

            // load the model
            characterEntity = Asset.Load<Entity>("character_00");
            characterEntity.Transformation.Rotation = Quaternion.RotationAxis(Vector3.UnitX, (float)(0.5 * Math.PI));
            characterEntity.Transformation.Translation = characterInitPos;
            Entities.Add(characterEntity);

            // create the stand
            var material = Asset.Load<Material>("character_00_material_mc00");
            var standEntity = CreateStand(material);
            standEntity.Transformation.Translation = new Vector3(0, 0, -80);
            standEntity.Transformation.Rotation = Quaternion.RotationAxis(Vector3.UnitX, (float)(0.5 * Math.PI));
            Entities.Add(standEntity);

            var standBorderEntity = CreateStandBorder(material);
            standBorderEntity.Transformation.Translation = new Vector3(0, 0, -80);
            standBorderEntity.Transformation.Rotation = Quaternion.RotationAxis(Vector3.UnitX, (float)(0.5 * Math.PI));
            Entities.Add(standBorderEntity);

            // create the lights
            var directLightEntity = CreateDirectLight(new Vector3(-1, 1, -1), new Color3(1, 1, 1), 0.9f);
            directionalLight = directLightEntity.Get<LightComponent>();
            Entities.Add(directLightEntity);

            var spotLightEntity = CreateSpotLight(new Vector3(0, -500, 600), new Vector3(0, -200, 0), 15, 20, new Color3(1, 1, 1), 0.9f);
            Entities.Add(spotLightEntity);
            spotLight = spotLightEntity.Get<LightComponent>();

            var rand = new Random();
            for (var i = -800; i <= 800; i = i + 200)
            {
                for (var j = -800; j <= 800; j = j + 200)
                {
                    var position = new Vector3(i, j, (float)(rand.NextDouble()*150));
                    var color = new Color3((float)rand.NextDouble() + 0.3f, (float)rand.NextDouble() + 0.3f, (float)rand.NextDouble() + 0.3f);
                    var light = CreatePointLight(position, color);
                    pointLights.Add(light.Get<LightComponent>());
                    pointLightEntities.Add(light);
                    Entities.Add(light);
                }
            }

            // set the camera
            var targetEntity = new Entity(characterInitPos);
            var cameraEntity = CreateCamera(cameraInitPos, targetEntity, (float)GraphicsDevice.BackBuffer.Width / (float)GraphicsDevice.BackBuffer.Height);
            camera = cameraEntity.Get<CameraComponent>();
            Entities.Add(cameraEntity);
            RenderSystem.Pipeline.SetCamera(camera);

            // UI
            CreateUI();

            // Add a custom script
            Script.Add(UpdateScene);
        }
Exemple #11
0
 protected abstract float ComputeScreenCoverage(CameraComponent camera, Vector3 position, Vector3 direction, float width, float height);
Exemple #12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SceneCameraSlot"/> class.
 /// </summary>
 /// <param name="camera">The camera.</param>
 public SceneCameraSlot(CameraComponent camera)
 {
     Camera = camera;
 }
Exemple #13
0
 public static void SetCamera(this RenderPass pass, CameraComponent camera)
 {
     pass.GetProcessor <CameraSetter>().Camera = camera;
 }
            public void Update(Entity entity, Vector3 virtualResolution)
            {
                var nearPlane = virtualResolution.Z / 2;
                var farPlane = nearPlane + virtualResolution.Z;
                var zOffset = nearPlane + virtualResolution.Z / 2;
                var aspectRatio = virtualResolution.X / virtualResolution.Y;
                var verticalFov = (float)Math.Atan2(virtualResolution.Y / 2, zOffset) * 2;

                var cameraComponent = new CameraComponent(nearPlane, farPlane)
                {
                    UseCustomAspectRatio = true,
                    AspectRatio = aspectRatio,
                    VerticalFieldOfView = MathUtil.RadiansToDegrees(verticalFov),
                    ViewMatrix = Matrix.LookAtRH(new Vector3(0, 0, zOffset), Vector3.Zero, Vector3.UnitY),
                    ProjectionMatrix = Matrix.PerspectiveFovRH(verticalFov, aspectRatio, nearPlane, farPlane),
                };

                Update(entity, cameraComponent);
            }
            public void Update(Entity entity, CameraComponent camera)
            {
                AspectRatio = camera.AspectRatio;
                FrustumHeight = 2 * (float)Math.Tan(MathUtil.DegreesToRadians(camera.VerticalFieldOfView) / 2);

                // extract the world matrix of the UI entity
                var worldMatrix = entity.Get<TransformComponent>().WorldMatrix;

                // rotate the UI element perpendicular to the camera view vector, if billboard is activated
                var uiComponent = entity.Get<UIComponent>();
                if (!uiComponent.IsFullScreen && uiComponent.IsBillboard)
                {
                    Matrix viewInverse;
                    Matrix.Invert(ref camera.ViewMatrix, out viewInverse);

                    // remove scale of the camera
                    viewInverse.Row1 /= viewInverse.Row1.XYZ().Length();
                    viewInverse.Row2 /= viewInverse.Row2.XYZ().Length();

                    // set the scale of the object
                    viewInverse.Row1 *= worldMatrix.Row1.XYZ().Length();
                    viewInverse.Row2 *= worldMatrix.Row2.XYZ().Length();

                    // set the adjusted world matrix
                    worldMatrix.Row1 = viewInverse.Row1;
                    worldMatrix.Row2 = viewInverse.Row2;
                    worldMatrix.Row3 = viewInverse.Row3;
                }

                // Rotation of Pi along 0x to go from UI space to world space
                worldMatrix.Row2 = -worldMatrix.Row2; 
                worldMatrix.Row3 = -worldMatrix.Row3;

                ProjectionMatrix = camera.ProjectionMatrix;
                Matrix.Multiply(ref worldMatrix, ref camera.ViewMatrix, out ViewMatrix);
                Matrix.Invert(ref ViewMatrix, out ViewMatrixInverse);
                Matrix.Multiply(ref ViewMatrix, ref ProjectionMatrix, out ViewProjectionMatrix);
            }
 public override void Start()
 {
     camera = Entity.Get<CameraComponent>();
     simulation = Entity.Get<PhysicsComponent>().Simulation;
 }