Maxime FRAPPAT

Hum …no thanks ! – Lordinaire

Tag: C# (Page 2 of 6)

[Xamarin] Hololens app with UrhoSharp : Spatial Mapping – Part 3

Today, we will try to detect our environment with the spatial mapping. That mechanism allows us to detect real life objects, like a floor or a table, and gets back information in order to build a virtual object that will be the representation of the real object.

Let me do it, please

First of all, we need to add the spatial mapping capability in our project by editing the Package.appxmanifest :

<Package   xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"   
           xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"   
           xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"   
           xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
           IgnorableNamespaces="uap mp">
 
 ...  
  <Capabilities>
    <span style="color: #ff0000;"><uap2:Capability Name="spatialPerception"/></span>
  </Capabilities>
</Package>

Override as always

The UrhoSharp framework has already handle basic scenario like spatial mapping in the class HoloApplication (the base class of or app). We only need to override some methods and enable some properties to be able to catch spatial mapping data. Our scenario will be very simple :

  • Start detection
  • Display detected objects (wireframe)
  • Tap to stop detection
  • Hide detected objects
  • Baaaaaaaaaaaaaaalls !

Let’s begin with the Start method :

private bool _isSpatialMappingActive;
 
private Node _detectedSurfaceNode;
 
protected override async void Start()
{
    base.Start();
 
    // Enable the AirTap gesture
    EnableGestureTapped = true;
 
    // Create a new node to store all the detected objects we will create
    _detectedSurfaceNode = Scene.CreateChild();
    _isSpatialMappingActive = true;
 
    // Start the detection
    await StartSpatialMapping(new Vector3(10, 10, 10));
}

For the Update method, just add a new condition :

protected override void OnUpdate(float timeStep)
{
    base.OnUpdate(timeStep);
 
    // Don't do anything if the detection is activated
    if (_isSpatialMappingActive)
        return;
 
    _spawnDeltaTime += timeStep;
    if (_spawnDeltaTime >= _spawnTimer)
    {
        _spawnDeltaTime = 0;
 
        var randomPosition = new Vector3(Randoms.Next(-0.3f, 0.3f), Randoms.Next(-0.3f, 0.3f), Randoms.Next(2, 5));
        CreateBall(randomPosition);
    }
}

The real stuff starting here. We will override the method OnSurfaceAddedOrUpdated like that :

public override void OnSurfaceAddedOrUpdated(string surfaceId, DateTimeOffset lastUpdateTimeUtc, SpatialVertex[] vertexData,
    short[] indexData, Vector3 boundsCenter, Quaternion boundsRotation)
{
    StaticModel model;

    // If the surface already exists get its node otherwise creates a new one
    var node = _detectedSurfaceNode.GetChild(surfaceId, false);
    if (node != null)
    {
        model = node.GetComponent<StaticModel>();
    }
    else
    {
        node = _detectedSurfaceNode.CreateChild(surfaceId);
        model = node.CreateComponent<StaticModel>();
    }

    // Set the position and rotation
    node.Position = boundsCenter;
    node.Rotation = boundsRotation;

    // The model is created with the vertex data
    model.Model = CreateModelFromVertexData(vertexData, indexData);

    // Add a rigidbody for the physic engine
    node.CreateComponent<RigidBody>();

    // Add a collision shape based on the model
    var shape = node.CreateComponent<CollisionShape>();
    shape.SetTriangleMesh(model.Model, 0, Vector3.One, Vector3.Zero, Quaternion.Identity);

    // Add a material for our model (a green wireframe)
    var material = Material.FromColor(Color.Green);
    material.FillMode = FillMode.Wireframe;
    model.SetMaterial(material);
}

To handle the Tap gesture, override the OnGestureTapped method :

public override void OnGestureTapped(GazeInfo gaze)
{
    base.OnGestureTapped(gaze);

    // Stop the detection
    _isSpatialMappingActive = false;
    StopSpatialMapping();

    // Disable wireframe models but keep the rest (RigidBody, CollisionShape)
    // to interact with it
    var childCount = _detectedSurfaceNode.GetNumChildren(false);
    for (uint i = 0; i < childCount; i++)
    {
        var childNode = _detectedSurfaceNode.GetChild(i);
        var model = childNode.GetComponent<StaticModel>();
        model.Enabled = false;
    }
}

That’s it folks !

[UWP] Data binding to method and from event

With the Windows 10 Anniversary update, XAML becomes smarter with some great features for the data binding. It is now possible to bind to a property or an event to a method very easily !

Here is a sample of different type of bindings (old and new) to compare the XAML synthax.

ViewModel

public class MainViewModel : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            OnPropertyChanged();
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {
        FirstName = "Maxime";
        LastName = "Frappat";
    }

    public string ToFullName(string firstName, string lastName)
    {
        return string.Concat(firstName, " ", lastName);
    }

    public void DeleteLastName()
    {
        LastName = string.Empty;
    }

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
    }

    #endregion
}

Helper class

public static class TextHelper
{
    public static string ToFullName(string firstName, string lastName)
    {
        return string.Concat(firstName, " ", lastName);
    }
}

View (Code-Behind)

public sealed partial class MainPage : Page
{
    public MainViewModel ViewModel { get; set; }

    public MainPage()
    {
        InitializeComponent();

        ViewModel = new MainViewModel();
        DataContext = ViewModel;
    }
}

View (XAML)

<!-- BINDING TO PROPERTIES -->
<StackPanel Orientation="Vertical">
    <TextBlock Text="BINDING TO PROPERTIES" Style="{StaticResource TitleStyle}"/>

    <TextBlock>
        <Run Text="{Binding FirstName}" />
        <Run Text="{Binding LastName}" />
    </TextBlock>
</StackPanel>

<!-- xBIND TO PROPERTIES -->
<StackPanel Orientation="Vertical">
    <TextBlock Text="xBIND TO PROPERTIES" Style="{StaticResource TitleStyle}" />

    <TextBlock>
        <Run Text="{x:Bind ViewModel.FirstName, Mode=OneWay}" />
        <Run Text="{x:Bind ViewModel.LastName, Mode=OneWay}" />
    </TextBlock>
</StackPanel>

<!-- xBIND TO INTERNAL METHODS -->
<StackPanel Orientation="Vertical">
    <TextBlock Text="xBIND TO INTERNAL METHODS" Style="{StaticResource TitleStyle}" />

    <TextBlock Text="{x:Bind ViewModel.ToFullName(ViewModel.FirstName, ViewModel.LastName), Mode=OneWay}" />
</StackPanel>

<!-- xBIND TO EXTERNAL METHODS -->
<StackPanel Orientation="Vertical">
    <TextBlock Text="xBIND TO EXTERNAL METHODS" Style="{StaticResource TitleStyle}" />

    <TextBlock Text="{x:Bind helper:TextHelper.ToFullName(ViewModel.FirstName, ViewModel.LastName), Mode=OneWay}" />
</StackPanel>

<!-- xBIND FROM EVENT -->
<StackPanel Orientation="Vertical">
    <TextBlock Text="xBIND FROM EVENT" Style="{StaticResource TitleStyle}" />

    <TextBlock Tapped="{x:Bind ViewModel.DeleteLastName}">
        <Run Text="{x:Bind ViewModel.FirstName, Mode=OneWay}" />
        <Run Text="{x:Bind ViewModel.LastName, Mode=OneWay}" />
    </TextBlock>
</StackPanel>

Result

Each bindings will display the same text.

anniverary_binding

Don’t forget to target the last SDK for your project.

So easy !

Page 2 of 6

Powered by WordPress & Theme by Anders Norén

%d bloggers like this: