Privacy in the Metaverse

Or: How to install any app on the Quest 3 without giving Meta your phone number.

With the long anticipated Apple Vision Pro become available at February 2nd 2024 (unfortunately only in the US), we’ll finally see Apple’s take on a consumer-ready headset for mixed reality – er … I meant to say spatial computing. Seamless video see-through and hand tracking – what a technological marvel.

As of now, the closest alternative to the Vision Pro, those unwilling to spend $3500 or located outside the US, seems to be the Meta Quest 3. And this only at a fraction of the price, at $500. But unlike Apple, Meta is less known for privacy-aware products. After all, it is their core business model to not be.

This post explains how to increase your privacy on the Quest 3, in four easy steps.

Continue reading Privacy in the Metaverse
A tour of C++ Cover Mountains

Eine Tour durch C++

English tl;dr: I helped with the German translation of Bjarne Stroustrup’s “A Tour of C++”. As it is about a German book, this post is in German as well

Nachdem Sie mit dem Buch “C++ Schnelleinstieg” einen praktikablen Einstieg in diese Sprache gewagt haben, können Sie als nächstes Ihr Wissen mit “Eine Tour durch C++” vervollständigen. Bei der Übersetzung dieses Buches vom Englischen ins Deutsche habe ich als Fachkorrektor mitgewirkt.

Dieses Buch deckt zahlreiche Features ab, die mit C++20 neu eingeführt wurden, darunter Module, Konzepte, Koroutinen und Bereiche. Selbst einige schon jetzt verfügbare Komponenten, die nicht vor C++23 in den Standard integriert werden sollen, werden vorgestellt.

Beim Verlag kaufen: Eine Tour durch C++

Über den Autor:

Bjarne Stroustrup hat C++ entwickelt und die erste Implementierung geschrieben. Derzeit ist er Professor an der Columbia University und hat zuvor bei AT&T Bell Labs an der Texas A&M University sowie bei Morgan Stanley gearbeitet.

Step Check Open Beta start

Wow, what a journey 🚀😎 . We are so proud to finally publicly share one of the things we have worked on since ioxp joined Vuforia: The new Vuforia Instruct feature Step Check™, starts its Public Beta now!

Based on the best-in-class Vuforia technology and developed together with many of the brightest people I know:

✴️Assisted quality assurance using #augmentedreality
✴️Integrated AI training
✴️Authoring from CAD

Next steps: learn from the beta feedback, improve and deliver!
Thank you, Vuforia, for how all of you welcomed us and for how we made things possible – together!

#ar #computervision #mixedreality #innovation #service #qualityassurance #acquisition

https://www.linkedin.com/posts/philipphasper_ioxp-augmentedreality-ai-activity-7004503199806013440-KJXV/

Update: Special video for the third Sunday of Advent 😉

https://www.linkedin.com/posts/philipphasper_ai-ar-vuforia-activity-7007691815181025280-6GEl

C++ Weihnachtsspecial

English summary: This is a holiday special for my German C++ beginners book, hence this post is in German.

Dieser Blogpost erweitert Ihr Wissen aus dem Buch C++ Schnelleinstieg: Programmieren lernen in 14 Tagen. Nach dem Kapitel 9 (Fortgeschrittene Konzepte) haben Sie alle nötigen Grundlagen, die Sie als Voraussetzung für das Special benötigen:

  • Grundlagen der Objektorientierung
  • Bibliotheken mittels vcpkg einbinden
  • GUI-Programmierung mit Nana
  • Callbacks und Lambdas
  • Fehler werfen

In diesem Projekt soll ein Weihnachtsbaum programmiert werden, bei dem Sie die Kerzen per Mausklick anzünden können. So sieht das ganze aus, wenn es fertig ist:

Das Codebeispiel beginnt in der main-Funktion des Programms und fügt nach und nach zusätzliche Komponenten oberhalb ein – daher beginnt die Zeilennummerierung so wie sie in der fertigen Datei sein wird (Downloadlink am Ende des Beitrags). Der Code wird durch Erklärungen unterbrochen, wie Sie an den fortlaufenden Zeilennummern erkennen können.

int main()
{
  nana::paint::image tree("tree.png");
  if (tree.empty())
  {
    /* Sie müssen die drei Bilder (tree.png, candleOn.png, candleOff.png) in den
       Build-Ordner kopieren. Sie finden diesen, indem Sie in Visual Studio im
       Projektmappen-Explorer einen Rechtsklick auf das Projekt durchführen und
       "Ordner in Datei-Explorer öffnen" auswählen.
    */
    throwException("Konnte das Baumbild nicht laden!");
  }

Die Funktion throwException wird später noch implementiert. Die für das Programm benötigten drei Bilder des Baums, der erloschenen und der angezündeten Kerze finden Sie zusammen mit der fertigen Codedatei am Ende dieses Beitrags. Nun geht es um die grundlegende Struktur: Das Fenster sowie die Kerzen.

  // Ein Nana-Formular als Basis des Fensters anlegen
  nana::form window(nana::API::make_center(tree.size().width, tree.size().height));
  window.caption("C++ Weihnachtsspecial 2021");

  // Jede Codezeile beim Anlegen der Liste entspricht einem Stockwerk des Baumes
  std::vector<Candle> candles = { Candle(290, 200),
    Candle(200, 300), Candle(300, 330), Candle(400, 315),
    Candle(120, 430), Candle(250, 450), Candle(350, 450), Candle(480, 450),
    Candle(70, 580), Candle(170, 620), Candle(290, 630), Candle(420, 625), Candle(540, 600) };

Die Kerzen werden als Objekte modelliert und mit x- und y-Koordinaten angelegt. Die Klassendefinition folgt gleich, nachdem der Rest der main-Funktion besprochen wurde. Folgen Sie solange dem im Abschnitt 4.3 des Buches erläuterten Prinzip des “Wishful Programmings” und nehmen Sie einfach an, Sie hätten schon eine solche Klasse, die Ihnen alle Wünsche erfüllt. Nun sollen die Grafiken alle gezeichnet werden:

  // Zeichnen des Baumes und der Kerzen
  nana::drawing drawing(window);
  drawing.draw([&](nana::paint::graphics& graphics)
  {
    tree.paste(graphics, nana::point(0, 0));
    for (Candle& candle : candles)
    {
      candle.draw(graphics);
    }
    graphics.string(nana::point(10, static_cast<int>(window.size().height) - 20),
      "C++ Schnelleinstieg: Programmieren lernen in 14 Tagen. Philipp Hasper, 2021");
  });

Das Zeichnen der weihnachtlichen Szenerie übernimmt die Nana-Klasse nana::drawing innerhalb einer Lambdafunktion. Sie sehen hier zwei praktische Methoden in Aktion:

  • tree.paste() kopiert das Bild des Baumes an eine bestimmte Stelle – hier an die Stelle (0,0) was der linken oberen Ecke entspricht. Hierfür muss auch noch das graphics-Object übergeben werden, welches aus dem Parameter der Lambdafunktion kommt.
  • graphics.string() schreibt einen Text an eine bestimmte Stelle. Hier soll es in die linke untere Ecke, daher muss für die y-Koordinate von der Höhe des Fensters genug Platz abgezogen werden, sodass der Text noch hinpasst.

Zum Schluss fehlen noch die Klickereignisse. Da die Kerzen als Grafiken gezeichnet wurden, haben sie kein automatisches Klick-Event wie zum Beispiel eine Schaltfläche. Daher muss jeder Klick ins Fenster abgefangen und darauf überprüft werden, ob seine Koordinaten innerhalb einer Kerze liegen:

  // Jeden Klick überprüfen, ob er eine Kerze trifft
  window.events().click([&](const nana::arg_click& event) {
    if (event.mouse_args == nullptr)
    {
      // Event kann nicht verarbeitet werden
      return;
    }
    for (Candle& candle : candles)
    {
      if (candle.isClicked(event.mouse_args->pos))
      {
        candle.toggle();
        /* Hier könnte man mit einem return die Schleife verlassen.
           Da sich aber bei falscher Platzierung die Kerzen überlagern
           könnten, wird die Schleife noch zu Ende geführt.
        */
      }
    }
    // Zeichnung wiederholen, sodass das Bild aktualisiert wird.
    drawing.update();
  });

  // Fenster anzeigen und Nana starten
  window.show();
  nana::exec();
  return 0;
}

Ob eine Kerze geklickt wurde, wird innerhalb der Candle-Klasse überprüft. Auch die Reaktion auf einen Klick ist in dieser Klasse implementiert. Fügen Sie daher diese Klasse oberhalb der main-Funktion ein:

// Klasse zum Zeichnen einer Kerze in zwei Zuständen (angezündet, ausgeblasen)
class Candle {
private:
  nana::paint::image candleOn;
  nana::paint::image candleOff;
  bool isOn = false;
  nana::rectangle rectangle;
public:
  Candle(int centerX, int centerY)
    : candleOn("candleOn.png"),
      candleOff("candleOff.png")
  {
    if (candleOn.empty() || candleOff.empty())
    {
      throwException("Konnte die Kerzenbilder nicht laden!");
    }
    if (candleOn.size() != candleOff.size())
    {
      throwException("Kerzenbilder haben nicht die gleiche Größe!");
    }
    /* Die an den Konstruktor übergebene x - und y - Koordinate sollen das Zentrum
       der Kerze angeben, daher müssen Sie für das Ziel-Rechteck umgerechnet werden.
     */
    nana::size size = candleOn.size();
    rectangle = nana::rectangle(centerX - size.width/2,
                                centerY - size.height/2,
                                size.width,
                                size.height);
  }

  void draw(nana::paint::graphics& graphics)
  {
    if (isOn)
    {
      candleOn.paste(graphics, rectangle.position());
    }
    else
    {
      candleOff.paste(graphics, rectangle.position());
    }
  }

  bool isClicked(const nana::point& point)
  {
    // Diese Methode testet, ob der Punkt innerhalb des Rechtecks liegt
    return rectangle.is_hit(point);
  }

  void toggle()
  {
    isOn = !isOn;
  }
};

Diese Klasse handhabt zwei verschiedene Bilder, die an einem Punkt zentriert angezeigt werden sollen. Abhängig vom Zustand (isOn), wird das eine oder das andere Bild gezeichnet. Zur einfacheren Platzierung wird beim Erzeugen des Objektes das gewünschte Zentrum der Kerze angegeben, was dann innerhalb des Konstruktors in ein entsprechendes Rechteck umgerechnet werden muss.

Nun fehlt noch eine Hilfsfunktion, die eine Fehlermeldung auf der Konsole ausgibt und dann einen Fehler wirft. Fügen Sie diese am Anfang der Datei, nach den include-Befehlen ein:

// Funktion, einen Fehler auf der Konsole ausgibt und dann einen Fehler wirft
void throwException(const std::string& msg)
{
  std::cout << msg << std::endl;
  throw std::exception(msg.c_str());
}

Und das war auch schon das ganze Programm. Frohe Feiertage und einen guten Rutsch! Den vollständigen Code und die Bilder können Sie hier herunterladen:

Jekyll plugin to bundle zip archives

The website for my recently published C++ book, cpp.hasper.info (German), was made with the static site generator Jekyll. It contains additional information, such as the code of all projects inside the book, as well as the sample solution for the exercises at the end of each chapter.

The reason why I used a static site generator in the first place was that I had all the code files organized in a folder, equipped with a CMake file which made sure the projects compile and are statically analyzed (I used cppcheck and cpplint, albeit with a very reduced set of checks due to the nature of the code examples). In order to not destroy this automation by copy-pasting code into a CMS, the site had to be generated around the code files.

I also wanted to enable the download of all C++ files as zip archive on a per-chapter basis. Again – I did not want to manually create this archive, in case I had to change some code in the future. So I wrote a Jekyll plugin which bundles given files into a zip archive which then can be placed behind a download link.

How it is used:

Filenames as multiple parameters:

{% zip archiveToCreate.zip file1.txt file2.txt %}

Spaces in filenames:

{% zip archiveToCreate.zip file1.txt folder/file2.txt 'file with spaces.txt' %}

A variable to contain a list of files is also possible:

{% zip ziparchiveToCreate.zip {{ chapter_code_files }} %}

The plugin code:

The plugin can be found here: https://github.com/PhilLab/jekyll-zip-bundler

# frozen_string_literal: true

# Copyright 2021 by Philipp Hasper
# MIT License
# https://github.com/PhilLab/jekyll-zip-bundler

require 'jekyll'
require 'zip'
# ~ gem 'rubyzip', '~>2.3.0'

module Jekyll
  # Valid syntax:
  # {% zip archiveToCreate.zip file1.txt file2.txt %}
  # {% zip archiveToCreate.zip file1.txt folder/file2.txt 'file with spaces.txt' %}
  # {% zip {{ variableName }} file1.txt 'folder/file with spaces.txt' {{ otherVariableName }} %}
  # {% zip {{ variableName }} {{ VariableContainingAList }} %}
  class ZipBundlerTag < Liquid::Tag
    VARIABLE_SYNTAX = /[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)/mx.freeze
    CACHE_FOLDER = '.jekyll-cache/zip_bundler/'

    def initialize(tag_name, markup, tokens)
      super
      # Split by spaces but only if the text following contains an even number of '
      # Based on https://stackoverflow.com/a/11566264
      # Extended to also not split between the curly brackets of Liquid
      # In addition, make sure the strings are stripped and not empty
      @files = markup.strip.split(/\s(?=(?:[^'}]|'[^']*'|{{[^}]*}})*$)/)
                     .map(&:strip)
                     .reject(&:empty?)
    end

    def render(context)
      # First file is the target zip archive path
      target, files = resolve_parameters(context)
      abort 'zip tag must be called with at least two files' if files.empty?

      zipfile_path = CACHE_FOLDER + target
      FileUtils.makedirs(File.dirname(zipfile_path))

      # Create the archive. Delete file, if it already exists
      File.delete(zipfile_path) if File.exist?(zipfile_path)
      Zip::File.open(zipfile_path, Zip::File::CREATE) do |zipfile|
        files.each do |file|
          # Two arguments:
          # - The name of the file as it will appear in the archive
          # - The original file, including the path to find it
          zipfile.add(File.basename(file), file)
        end
      end
      puts "Created archive #{zipfile_path}"

      # Add the archive to the site's static files
      site = context.registers[:site]
      site.static_files << Jekyll::StaticFile.new(site, "#{site.source}/#{CACHE_FOLDER}",
                                                  File.dirname(target),
                                                  File.basename(zipfile_path))
      # No rendered output
      ''
    end

    def resolve_parameters(context)
      # Resolve the given parameters to a file list
      target, files = @files.map do |file|
        next file unless file.match(VARIABLE_SYNTAX)

        # This is a variable. Look it up.
        context[file]
      end

      [target, files]
    end
  end
end

Liquid::Template.register_tag('zip', Jekyll::ZipBundlerTag)

C++ Schnelleinstieg

English summary: I have published a C++ beginners book. As it is in German, so is this post.

Auf 304 kompakten Seiten bekommen Sie einen fundierten Einstieg in die C++ Programmierung. Besonders hervorzuheben und für ein Einsteigerbuch ungewöhnlich ist die Einbindung von diversen Open-Source-Bibliotheken – professionell gemanaged mittels des Paketmanagers vcpkg. Im Rahmen des Buches erlernen Sie auch fortgeschrittene Fähigkeiten wie die Programmierung graphischer Oberflächen und das Arbeiten mit Web-APIs, wie der von Wikipedia. Das Buch legt wert auf einen verständlichen aber dennoch professionellen Programmierstil und gibt einen Ausblick über das Programmieren im professionellen Umfeld, sowie Anküpfungspunkte zum selbstständigen Erweitern der eigenen Kenntnisse.

Das Begleitmaterial sowie das Inhaltsverzeichnis und eine Leseprobe finden Sie auf https://cpp.hasper.info/

Beim Verlag kaufen: C++ Schnelleinstieg

Bei Amazon kaufen: C++ Schnelleinstieg

Natürlich freue ich mich sehr über eine Bewertung des Buches.

Buchcover C++ Schnelleinstieg, Philipp Hasper
ISBN: 9783747503225
  • Alle Grundlagen einfach erläutert
  • Objektorientierte Programmierung
  • Einsatz von Open-Source-Bibliotheken
  • Grafische Benutzungsoberflächen (GUI)
  • Internetanfragen und JSON-Parsing
  • Zeiger und virtuelle Methoden
  • Fehlersuche und Debugging
  • Moderner Programmierstil
  • Programmcode, Lösungen und Glossar zum Download

»Während viele andere C++-Lehrbücher ihr Heil in der Erklärung jedes noch so obskuren Details suchen, fokussiert sich Hasper auf alles Wichtige, das Entwicklerinnen und Entwickler bei der alltäglichen Arbeit brauchen. Haben sie bisher mit Sprachen wie JavaScript oder C# Erfahrung gesammelt, findet sich hier ein hilfreicher Wegweiser durch den Dschungel des modernen C++.«

(Heise Online, 09/2021)

»Didaktisch gut aufgebaut.«

(ekz Bibliotheksservice, 10/2021)

Choir practice during the Covid-19 Pandemic

Singing proved to be a challenge during the Corona pandemic, both because of hygienic considerations and contact restrictions.

While conferencing software like Zoom, Teams or Skype worked for other types of remote collaboration, making music together proved to be impossible due to the latency – having musicians with more than 0.5 seconds delay trying to interact with each other does not work. Some choirs resort to a many-to-one setting, meaning that everyone is muted except the choir director who plays the score on the piano. This results in everyone singing at home alone, just listening to the piano – and the director is fully on their own, with occasional questions like “did anyone have a problem here?” left as their only feedback channel.

Still, this is better than not making music at all, but luckily there is a better (but way more compilated) solution for it. We used the open source tools Jamulus and Jitsi.

Jamulus for audio

Jamulus is a software dedicated to low-latency audio connections over the internet. You can set up your own server but we used a paid service for this, as it proved to be fast and reliable: https://melomax.live/booking/ It is even possible to just start the server on your home PC, but the experience in terms of quality and latency was much worse!

I would only recommend to start a server on your home PC for the first phase and this is the “getting everyone up to speed” phase, which can take a couple of hours. Unfortunately, Jamulus is not an out-of-the-box solution and often has issues with the audio hardware.

  1. Have every singer read through the setup guide here: https://jamulus.io/wiki/Getting-Started . At the very minimum they need cable headphones. An Ethernet cable and an external microphone are highly recommended.
  2. Have a tech-savvy person available who starts a preliminary server on their PC, because it is cheaper there.
  3. Invite the other singers in small groups (more than four would likely overburden your preliminary server) to test their setup. Prepare for a couple of hours of tech support here (again, you need at least one tech-savvy person for this). In general, you need to experiment which audio settings work for each individual.
  4. When you are confident that the singers are ready, book your sessions on Melomax and invite the whole group there.
  5. Be prepared for additional tech support and expect that setups which worked previously might miraculously break later.

The setup can be very frustrating, but the outcome was really worth it – we had a really emotional situation when we were able to digitally sing together after months of silence!

Jitsi for video

Jamulus only supports audio and it is already sufficient for making music together. But the main drawback is that you still need a “main reference” i.e., a piano to sing along. This is needed to cope with the minimal but still existing latency – everyone has to concentrate on it so the choir does not get slower and slower.

But good news! It is possible to also enable low-latency video, using a second software named Jitsi. The trick is to setup your own Jitsi server and configure it to be low-latency. In essence, this means to

  1. Reduce the video quality to a bare minimum (e.g. 320 x 190 pixels)
  2. Disable Jitsi audio
  3. Disable encryption

To achieve this, you need to setup a Jitsi server (this time, an existing service won’t be enough as you need detailed control over the instance). Then, add the following settings to the /etc/jitsi/meet/<server name>-config.js :

{
    testing: {
      disableE2EE: true,
      p2pTestMode: false
    },
    enableNoisyMicDetection: false,
    startWithAudioMuted: true,
    startSilent: true,
    stereo: true,
    disableAP: true,
    resolution: 200, 
    constraints: {
      video: {
        aspectRatio: 16 / 9,
        height: {
          ideal: 200,
          max: 200,
          min: 100
        }
      }
    },
    enableLayerSuspension: true,
    prejoinPageEnabled: false,
    p2p: {
      enabled: false,
    }
  }

The result

This technical setup allowed us to come together during the lock down(s) and hence, we were able to record the result quickly when we were allowed to meet again. Still, with unusually large gaps between every singer, but at least physical again:

You can find more about the choir (in German) on https://vokalspezial.de/

How to remove Unity Services from your project

I recently found myself in the situation that a Unity project which changed ownership, still linked to now non-existing Unity Services. It constantly gave me warning messages like:

Unable to access Unity services. Please log in or request membership to this project to use these services

These were only warnings, so nothing particularly dangerous, but still I wanted to get rid of them. As it took me multiple iterations to solve it and there are multiple unsolved or incomplete post about it, I wanted to share here how I finally managed to solve this problem:

  1. Navigate to the file <Your project folder>/ProjectSettings/ProjectSettings.asset and open it with a text editor
  2. In the section cloudServicesEnabled, set all to 0
  3. Set the cloudProjectId empty (i.e. a space after the colon)

Especially the space part took me a while to figure it out. The respective sections of the Unity project’s settings file now look similar to this:

...  
  vrEditorSettings:
    daydream:
      daydreamIconForeground: {fileID: 0}
      daydreamIconBackground: {fileID: 0}
  cloudServicesEnabled:
    UNet: 0
  luminIcon:
    m_Name: 
    m_ModelFolderPath: 
    m_PortalFolderPath: 
  luminCert:
    m_CertPath: 
    m_SignPackage: 1
  luminIsChannelApp: 0
  luminVersion:
    m_VersionCode: 1
    m_VersionName: 
  apiCompatibilityLevel: 6
  cloudProjectId: 
  framebufferDepthMemorylessMode: 0
...

Having these modifications in the project settings made the warning disappear, as the unused cloud services are now properly removed from the project. However, given that Unity sometimes changes their framework and configurations, this might get out-dated some day in the near future. If this doesn’t work anymore when you are trying it, please drop me a message, so I can retry and update the article. But so far, the warning has not popped up again in any of my Unity projects.

Custom backgrounds in Microsoft Teams video calls

Update: In newer versions of Teams, a button has been added to add custom images: Just go to the Background settings and click “+ Add New”.

Microsoft Teams allows you to blur or replace the background with pre-defined images. However, there is no user interface which allows to upload custom images.

It turns out that it is just the upload button which is missing – if you know where the folder lies from which Teams fetches the backgrounds, you can easily place your own content in there.

  • On Windows, this is: %AppData%\Microsoft\Teams\Backgrounds\Uploads, i.e. C:\Users\<your user name>\AppData\Roaming\Microsoft\Teams\Backgrounds\Uploads
  • On Mac, this is: /users/<your user name>/Library/Application Support/Microsoft/Teams/Backgrounds/Uploads

This allows for some creative effects:

To subtle? What about this:

P.S.: One nifty trick to bewilder your colleagues: Take a screenshot during the next video call, use Gimp to retouch the person from the image and use their room as your background

(extra blur added for this screenshot)

How we organize a small development team with minimal overhead in Gitlab

At ioxp we develop a novel way of sharing knowledge using Augmented Reality, AI and Computer Vision to change the way how industries operate. Our ecosystem consists of many different tools and technologies – the AI backend, Hololens apps , Android apps, a web-based editor and cloud services – so we are very dependent on an efficient way to organize our developers and their different, often overlapping sub-teams.

And after all we are a startup and engineers by heart – we have to keep the balance between a thorough designed process and the creative freedom needed to explore and create new things.

This post will concentrate on how we facilitate developer communication and team work using features of the DevOps tool Gitlab, factoring out all other important stages like product management, planning or sprint planning. It is an excerpt from our internal Handbook (naturally hosted as Wiki in Gitlab) and describes the process I have designed and implemented together with my team.

The development process is primarily guided by issues and their three most important attributes: labels, assignees and related merge requests. It uses as little as 10 different issue labels and is divided into six stages:

Continue reading How we organize a small development team with minimal overhead in Gitlab