Clirio Scan Share

Scan Share ist eine Funktion der Clirio-Produktreihe zum schnellen Teilen von Photogrammetrie-Scans und deren Anzeige in einem Webviewer.


Clirio Scan Share

Typ

Unity App Feature + Website

Jahr

2023

Angewandte Fähigkeiten

  • Blazor
  • Back-End
  • Front-End

Unternehmen

Clirio

Übersicht

Mit der Clirio Scan Share-Funktion können Nutzer einen mit der Clirio Scan-App erfassten Photogrammetrie-Scan schnell teilen. Die Funktion besteht aus Freigabeoptionen in der in Unity entwickelten Clirio View-App und einem in Blazor und Three.js entwickelten Webviewer, über den geteilte Scans betrachtet werden können. Durch Drücken der Freigabe-Schaltfläche in einer der Clirio View-Apps öffnet sich ein Pop-up zur Konfiguration der Freigabe. Der Nutzer kann Ablaufzeit und Passwortschutz festlegen und verlangen, dass der Betrachter in den Arbeitsbereich eingeladen wurde – in diesem Fall muss sich der Empfänger mit seinem Clirio-Konto anmelden, um den Scan anzuzeigen.

Sobald der Nutzer mit den ausgewählten Optionen zufrieden ist, kann er den Link erstellen und ihn entweder in die Zwischenablage kopieren oder die plattformeigene Freigabefunktion nutzen. Unter iOS öffnet sich der Freigabedialog mit kompatiblen Apps wie Messengern, unter Windows die Mail-App. Es gibt auch eine Option, ein formatiertes eingebettetes Iframe-Element für die Freigabe zu konfigurieren und zu generieren.

Der Webviewer soll so leichtgewichtig und zugänglich wie möglich sein. Der Scan kann aus verschiedenen Blickwinkeln betrachtet werden; Metadaten und eine Maßstabslegende werden angezeigt, und Schaltflächen erlauben das Ein- und Ausblenden von Elementen.

Ein interaktives Beispiel für eine eingebettete Scan-Freigabe. Klicken und ziehen, um das Modell aus verschiedenen Blickwinkeln zu betrachten. Dieselbe Freigabe ist auch auf einer eigenen Seite erreichbar.

Entwicklung

Für die Entwicklung dieser Funktion gab es zwei Bereiche: die clientseitige Share-UI und -Logik sowie den Webviewer.

<h3>Share-UI und Logik</h3>
<p>Entwickelt mit C# in Unity als Pop-up, erreichbar über die Beobachtungsdetails eines beliebigen Scans in der Clirio View-App. Die UI wurde in Figma in Zusammenarbeit mit <a href="https://www.linkedin.com/in/jordan-wischmann-32a4b380/" target="_blank">Jordan Wischmann</a> entworfen.</p>

<h3>Webviewer</h3>
<p>Entwickelt mit Blazor und Three.js. Das Backend-Token-Sharing implementierte <a href="https://www.linkedin.com/in/timthibault/" target="_blank">Timothy Thibault</a>.</p>
Clirio Scan Share – Freigabe-UI in Clirio View
Share-UI
Clirio Scan Share – Embed-Konfigurations-UI
Embed-UI
Clirio Scan Share – Webviewer mit 3D-Scan
Webviewer

Share-UI und Logik

Die Share-UI durchlief mehrere Iterationen. Ursprünglich für MRTK2 mit wenigen Optionen entwickelt, wurde die Funktion später um Ablaufzeit, Passwortschutz und Embed-Funktionen erweitert. 2023 wurde die UI in Zusammenarbeit mit Jordan Wischmann überarbeitet, als die Clirio View-App auf MRTK3 migriert wurde.

Die Backend-API zur Link-Generierung implementierte Timothy Thibault. Das zurückgegebene Ergebnis enthält eine URL, die der Client dem Nutzer anzeigen und in die nativen Freigabeoptionen jeder Plattform einbinden kann.

Die größte Herausforderung war die UX-Gestaltung, da es viele Optionen für den Nutzer gab. Dazu gehörte die Möglichkeit, Freigaben privat zu machen und das Teilen intern auf Teams in Arbeitsbereichen zu beschränken – was einen zusätzlichen Authentifizierungsfluss im Webviewer erforderte. Die Vielzahl an Optionen erschwerte auch ein kompaktes UI. Ich nutzte dynamisches Design und Layouts, damit versteckte Optionen wie Ablaufverlängerung oder Passwortänderung elegant ein- oder ausgeblendet werden konnten. Die Logik-Entwicklung war vergleichsweise unkompliziert; die größere Herausforderung war zu definieren, wie die Funktion überhaupt arbeiten soll. Nach jeder Nutzerinteraktion aktualisiert sich die UI entsprechend der Freigabekonfiguration und aller anderen Zustände. Gibt es eine passende bestehende Freigabe, wird der vorhandene Link angezeigt, andernfalls die Schaltfläche zum Erzeugen einer neuen Freigabe.

Eine interessante Änderung meiner Controller-Herangehensweise testete ich erstmals in diesem Feature – nach einer Diskussion mit meinem Teamkollegen Tonio Stillman. Zuvor verdrahtete ich OnClick()-Events direkt im Unity-Inspector. Hier setzte ich stattdessen alle Event-Listener im Controller selbst, hielt die Listener-Funktionen privat und vereinfachte die Inspector-Einrichtung so, dass jede Schaltfläche nur einmal dem Controller zugewiesen wird und keine weiteren Referenzen nötig sind. Das macht die Einrichtung im Editor weniger fehleranfällig und wartbarer, da weniger Code-Verständnis erforderlich ist.

Animierter UX-Walkthrough der Clirio Scan Share-Freigabe
Share-UX-Walkthrough

Webviewer

Die Implementierung des Webviewers war die komplexere Aufgabe, da er eigenständig ist und die meiste Funktionalität von Grund auf neu entstehen musste. Der Viewer wurde in C# mit Blazor und JavaScript mit Three.js geschrieben und nutzt Tailwind CSS für das Styling. Ich hatte zuvor mit Three.js gearbeitet und ein Großteil meiner Arbeit drehte sich um die korrekte Einrichtung des Viewers.

Zu den Herausforderungen bei Three.js gehörten das Laden der in der MTL-Datei definierten Texturdateien mit korrekten SAS-Tokens, die Unterstützung verschiedener MTL-Formate und Materialeigenschaften sowie ein dynamischer Viewer, der unterschiedliche Modellgrößen und -formen auf vielen Geräten unterstützt.

Code-Beispiel

ThreeJSFunctions.js
// CODE SNIPPET, NOT THE FULL FILE ////______________________________________________________________________ Loadfunction loadModel(OBJFile, MTLFile, SasUrls) {    //Load with MTL + Textures    //Note: The MTLLoader has been customized so that it can have a SAS token specified,     //      and that it converts the filenames in the MTL file to lowercase(otherwise Azure Storage URL will be incorrect)    var mtlLoader = new MTLLoader();     mtlLoader.setSasUrls(SasUrls);    mtlLoader.load(MTLFile, function (materials) {            materials.preload();            new OBJLoader()                 .setMaterials(materials)                .load(OBJFile, function (object) {                    object.traverse(function (child) {   // aka setTexture                        if (child instanceof THREE.Mesh) {                            child.material.side = THREE.DoubleSide;                            //The default color space is sRGB, we need Linear (will be too dark otherwise)                            //see https://threejs.org/docs/#manual/en/introduction/Color-management                            if (child.material.map) child.material.map.encoding = THREE.LinearEncoding;                        }                    });                    object.rotation.y = Math.PI; //rotate 180 degrees to match orientation in Clirio View                    //Calculate y center pos, raise model and recenter camera on model                    var boundingBox = new THREE.Box3().setFromObject(object);                     size = new THREE.Vector3();                    boundingBox.getSize(size);                    var center = new THREE.Vector3();                    boundingBox.getCenter(center);                    var centerPos = object.localToWorld(center);                    objYCenterPos = size.y / 2 - centerPos.y + gapAboveGrid;                    longestSide = Math.max(size.x, size.y, size.z);                    object.position.set(centerPos.x, objYCenterPos, centerPos.z);                    //for large objects, update view + grid                    configureGridBasedOnModelSize();                    resetView();                    //add to scene, add scale legend, remove spinner                    scene.add(object);                    addScaleLegend();                    scanSpinner.classList.add('hidden');                                    }, function (item) {                    //console.log("Load Progress: " + item.loaded + "/" + item.total);                }, function (error) {                    console.error(error);                });        });        animate();}

View gist on GitHub

Diese Portfolio-Website wurde entwickelt mit Astro. Urheberrecht © 2026 Robert Michels. Alle Rechte vorbehalten.
Lebenslauf Datenschutz