Project and Unproject
This C# Intermediate tutorial covers projecting and unprojecting coordinates from 3D to 2D and vice versa.
Explanation
When we want to 'convert' 3D coordinates to a 2D screen, we speak 'Projecting'. The other way around is called 'Unprojecting'. Both scenarios are fairly common in 3D games.
The 3D to 2D or projecting happens for instance when you have a 3d quest marker. When the target you need to travel to is somewhere in front of you in the world, then you want to draw a 2D quest marker on screen that gives you an indication of where in the 3D world that target is located.
From 2D to 3D is often used to convert a mouse coordinate into the looking direction of the camera. This can be used for firing a weapon or setting a target on a map when playing a strategy game.
Project
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using Stride.Core.Mathematics;
using Stride.Engine;
using Stride.Graphics;
namespace CSharpIntermediate.Code
{
public class ProjectDemo : SyncScript
{
public Entity projectSphere;
public Entity projectSphereChild;
private CameraComponent camera;
public override void Start()
{
camera = Entity.Get<CameraComponent>();
}
public override void Update()
{
var backBuffer = GraphicsDevice.Presenter.BackBuffer;
var sphereProjection = Vector3.Project(projectSphere.Transform.WorldMatrix.TranslationVector, 0, 0, backBuffer.Width, backBuffer.Height,0, 8, camera.ViewProjectionMatrix);
var sphereChildProjection = Vector3.Project(projectSphereChild.Transform.WorldMatrix.TranslationVector, 0, 0, backBuffer.Width, backBuffer.Height,0, 8, camera.ViewProjectionMatrix);
// Similar method using Viewports
//var viewport = new Viewport(0, 0, backBuffer.Width, backBuffer.Height);
//var sphereProjection = viewport.Project(projectSphere.Transform.WorldMatrix.TranslationVector, camera.ProjectionMatrix, camera.ViewMatrix, Matrix.Identity);
//var sphereChildProjection = viewport.Project(projectSphereChild.Transform.WorldMatrix.TranslationVector, camera.ProjectionMatrix, camera.ViewMatrix, Matrix.Identity);
DebugText.Print($"Parent", new Int2(sphereProjection.XY()));
DebugText.Print($"Child", new Int2(sphereChildProjection.XY()));
}
}
}
Unproject
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using Stride.Core.Mathematics;
using Stride.Engine;
using Stride.Graphics;
using Stride.Input;
using Stride.Physics;
namespace CSharpIntermediate.Code
{
public class UnprojectDemo : SyncScript
{
private CameraComponent camera;
public Entity sphereToClone;
public override void Start()
{
camera = Entity.Get<CameraComponent>();
}
public override void Update()
{
if (Input.IsMouseButtonPressed(MouseButton.Left))
{
var backBuffer = GraphicsDevice.Presenter.BackBuffer;
var viewport = new Viewport(0, 0, backBuffer.Width, backBuffer.Height);
var nearPosition = viewport.Unproject(new Vector3(Input.AbsoluteMousePosition, 0.0f), camera.ProjectionMatrix, camera.ViewMatrix, Matrix.Identity);
var farPosition = viewport.Unproject(new Vector3(Input.AbsoluteMousePosition, 1.0f), camera.ProjectionMatrix, camera.ViewMatrix, Matrix.Identity);
var hitResult = this.GetSimulation().Raycast(nearPosition, farPosition);
// If there is a hitresult, clone the sphere and place it on that position
if (hitResult.Succeeded)
{
var sphereClone = sphereToClone.Clone();
sphereClone.Transform.Position = hitResult.Point;
Entity.Scene.Entities.Add(sphereClone);
}
}
}
}
}