Fontys VR-Cave Daemon

From Fontys VR-Wiki
Jump to: navigation, search

Concept

De Fontys VR-Cave Daemon, of kortweg daemon is bedoeld om het gebruik van hardware te vergemakkelijken. Iedere applicatie heeft input nodig. Deze input bestaat normaliter uit tracking informatie vanuit de flock of birds of anders uit wii-mote controller informatie (er zijn alternatieven mogelijk).

De oorspronkelijke opzet was dat iedere applicatie (of applicatie platform) zelfstandig zou communiceren met de drivers om zijn input te vergaren. Het is de doelstelling van de daemon om dit een beetje uit handen te nemen.

Basis functionaliteit deamon:

  • Alle input informatie wordt via UDP gebroadcast over het gehele netwerk.

Aanvullende functionaliteit daemon:

  • Stereoscopische projecties worden in de daemon applicatie berekend en ook doorgestuurd (samen met de input informatie).
  • De daemon biedt de mogelijkheid tot data synchronisatie

Tevens bevat ieder pakketje een timestamp. Het gebruik hiervan maakt het mogelijk om eenvoudig tijdsafhankelijke "tijd->scene" demo's te maken.


De applicatie

De Fontys VR-Cave Daemon staat in de directory:

D:\Cave software\wouter\fontysvr\daemon

De executable daemon.exe dient gebruikt te worden om de applicatie te starten. De volgende dll's zijn benodigd om dit te doen:

  • Bird.dll
  • SDL_image.dll
  • libpng12-0.dll

De laatste twee dll's staan in die directory.

Compilatie

De applicatie D:\Cave software\wouter\fontysvr\daemon is vervaardigd met Visual Studio 2008. Het project kan geopend worden door daemon.sln te openen (met VS2008). Er is een release en een debug configuratie.

De release configuratie maakt gebruik van multithreaded static C++ runtime libraries. (Er zijn dus geen service packs nodig om de applicatie te kunnen draaien.) De debug configuratie maakt gebruik van de dynamische variant.

De Wii-mote driver (Wiiyourself) die bij het project inbegrepen is, maakt op een foutieve manier gebruik van debug assertions. Deze debug assertions veroorzaken in debug mode onterechte inconsitentie meldingen (op zeer incidentele basis). (In release mode worden ze genegeerd en veroorzaken ze dus geen problemen.)

Het project heeft een aantal afhankelijkheden:

  • C:\FOBSDK\
  • ../../common/
  • ../../SDL-1.2.14/
  • ../../SDL_image-1.2.10/
  • ../../boost_1_42_0/

Merk op dat de statische libraries voor boost in de project directory zijn opgenomen.

De common library is af en toe onderhevig aan verandering (deze library wordt ook in andere projecten gebruikt). Het is echter zeer onwaarschijnlijk dat deze veranderingen de functionaliteit van de daemon zullen beïnvloeden (het mag niet). Alle libraries worden statisch gelinkt of opgenomen in de programma directory (met uitzondering van Bird.dll).

Gebruik applicatie

Voordat de Daemon kan starten moet eerst de Flock of Bird aan staan. (Anders wordt er een melding gegeven.) Een andere vereiste is dat er geen andere applicaties zijn die de FOB uitlezen. Indien de daemon opgestart is, wordt er een plaatje van de cave getoond. Hierin worden twee assen-stelsels getoond die de bril en de wand moeten voorstellen (Deze assen-stelsels kunnen (deels) verborgen zijn.)

Onderaan de daemon zitten twee knoppen: Calibrate en Connect Wiimote. De "calibrate" knop is in zijn algemeenheid niet nodig. De gebruiker kan hiermee de post-processing aanpassen die wordt toegepast op de FOB input. De calibratie wordt opgeslagen in calibration.dat. De calibratie wordt gelezen bij het opstarten en weggeschreven bij het afsluiten.

Door de calibration.dat te verwijderen, wordt de identiteits configuratie gebruikt. Er is een backup van de correcte calibratie gemaakt: calibration.dat.correct.

Indien een wiimote verbonden is met de windows Human Interface Device component, dan kan de wiimote verbonden worden met de daemon door op "Connect Wiimote" te klikken. De wiimote kan vervolgens getest worden door op A en B te drukken. (Indien het werkt, dan worden deze knoppen weergegeven in het scherm rechtsboven.) De nunchuck kan gebruikt worden door deze in te pluggen in de wiimote. De coördinaten van de joystick worden weergeven.

In het bestand daemon.log worden mogelijke complicaties gelogd. Indien er iets niet werkt, dan kan het raadplegen van dit bestand in sommige gevallen voor opheldering zorgen.

Protocol

Daemon naar Clients

Een flink aantal keren per seconden (+- 80), broadcast de daemon een UDP datagram over het cave subnet. Omdat UDP een stateless protocol is, hoeft er geen verbinding opgezet te worden.

Bij een UDP broadcast, wordt het datagram in principe gewoon op de lijn gezet. Alle machines op het netwerk zijn in staat de informatie eraf te pakken.

Wanneer de daemon aan het uizenden is, kunnen alle machines de data uitlezen op UDP poort 8881. Indien de poort niet uitgelezen wordt, dan heeft het OS een eindige buffer (de data gaat dus gewoon verloren).

Omdat UDP stateless is, werkt de daemon in principe ook stateless. In principe maakt het niet uit of je eerst de applicatie start en daarna de hardware/daemon. De annotatie "in principe" is gebruikt, omdat het mogelijk is om een client-applicatie te construeren waarvoor dit niet geld.

Het protocol wordt hieronder omschreven. In de meeste gevallen is het niet nodig om het protocol te kennen omdat er componenten beschikbaar zijn die dit afhandelen (zie Fontys VR-Cave Daemon SDK).


PACKET FORMAT:

  • GUARD (int32)
  • WHICH_MESSAGE (int32)
  • MESSAGE


DEFAULT_MESSAGE FORMAT:

time

  • MILISECONDS (unsigned long long,64bit)

eyes

  • LEFT (vec3)
  • RIGHT (vec3)

head

  • ORIGIN (vec3)
  • ORIENTATION (radialvec3)

wand

  • ORIGIN (vec3)
  • ORIENTATION (radialvec3)

projections

  • LEFT/LEFT (mat44)
  • LEFT/RIGHT (mat44)
  • FRONT/LEFT (mat44)
  • FRONT/RIGHT (mat44)
  • RIGHT/LEFT (mat44)
  • RIGHT/RIGHT (mat44)
  • BOTTOM/LEFT (mat44)
  • BOTTOM/RIGHT (mat44)

wiimote

  • WIIMOTE_BUTTONS (uint16)
  • NUNCHUCK_BUTTONS (uint16)
  • NUNCHUCK_JOY (vec2)



Hierboven wordt in feite de structuur van een datagram gedefinieerd. Het datagram formaat is binair. Bovenaan wordt de algehele stuctuur gedefinieerd (PACKET_FORMAT).

De eerste waarde GUARD, is een constante (1938373234) waarmee een daemon pakket zich geïdentificeerd. Mochten andere processen pakketten UDP pakketten sturen naar poort 8881, dan worden deze hoogstwaarschijnlijk genegeerd omdat de eerste 4 bytes waarschijnlijk anders zijn. (Het is discutabel of een dergelijke constructie ook daadwerkelijk nuttig is.)

De tweede waarde geeft aan wat voor bericht er verstuurd wordt. Er zijn hier twee opties:

  • DAEMON_MESSAGE_DEFAULT (gedefinieerd als 0)
  • DAEMON_MESSAGE_CUSTOM (gedefinieerd als 1)

De structuur van een default bericht is gedefinieerd onder DEFAULT_MESSAGE FORMAT. De structuur van een custom bericht is ongedefinieerd. De maker van de Cave applicatie mag dit zelf bedenken. Custom berichten worden gebruikt voor de synchronisatie van applicatie data.

Er is een bovengrens voor de grootte van UDP pakketten. Het totale pakket mag niet groter zijn dan 65507 bytes. Dit betekent dat het onderdeel MESSAGE niet groter mag zijn dan 65507 - 8 = 65499 bytes.

De variabelen zijn binair, en worden in intel representatie opgeslagen. Voor integers betekent dit dat de little endian representatie gebruikt wordt. Hoe de bytes geordend zijn voor floating point getallen is (mij) niet bekend. (Zolang je geen vage machine gebruikt om de data uit te lezen zou alles goed moeten gaan.)

Hieronder worden de types omschreven:

uint16: 16 bits signed integer

int32: 32 bits signed integer

unsigned long long: 64 bits unsigned integer

vec2: Positie vector, twee single precision floating point getallen

vec3: Positie vector, drie single precision floating point getallen

radialvec3: Radiaal vector, drie single precision floating point getallen (zie *****)

mat44: 4x4 homogene matrix bestaande uit 16 single precision floating point getallen. De matrix wordt opgelsagen in column-major format. Dit betekent dat de eerste 4 getallen, de eerste colom van de matrix voostellen. De betekenis van de matrices wordt uitgelegd in (**********).

Het default bericht wordt opgesteld in message.cpp in de functie compose(). Indien het protocol aangepast zou moeten worden, dan zou er bij voorkeur iets aangeplakt moeten worden. Dit heeft dan namelijk geen effect op bestaande cave-daemon applicaties. De daemonSDK (zie Fontys VR-Cave Daemon SDK) negeert gewoonweg data die volgt "na de definitie".

Synchronisatie

Een ander aspect van het protocol is dat het ook mogelijk is om berichten naar de daemon te sturen. De daemon zelf draait op poort 1023. Iedere machine kan een bericht sturen met custom data naar de daemon. De daemon zorgt vervolgens dat het bericht wordt doorgepaast naar alle machines doormiddel van een broadcast. Alle machines, (ook de machine die het bericht stuurt) krijgen het bericht dus weer terug.

In de praktijk zal het niet vaak voorkomen dat het nodig is dat cave machines (voor/links/rechts/onder) berichten naar de daemon sturen. Normaliter is er een client/server architectuur. De server applicatie stuurt dan de applicatie data, en alle machines ontvangen deze data weer.

Toch blijft de mogelijkheid open om clients data te laten sturen. Op deze manier zou je client machines gezamelijk berekeningen kunnen laten doen die worden gebruikt in de simulatie (indien het rekenkundig bewerkelijk is).

Een andere mogelijkheid is om van buiten de cave data te sturen naar de daemon. Deze data wordt vervolgens weer gedistribueerd over het cave netwerk. Zo moet het mogelijk zijn om bijvoorbeeld skeletal animation data uit het lokaal ernaast door te sturen naar de daemon waardoor deze data gebruikt kan worden in een cave applicatie.

Door een DAEMON_MESSAGE_CUSTOM bericht te sturen naar de daemon, wordt dit bericht gewoonweg gebroadcast. Dus, indien je een dergelijk bericht stuurt naar de daemon (poort 1023, 192.168.0.1), dan komt de inhoud van dit bericht aan bij alle machines in de cave op poort 8881. (Het bericht wordt niet gewijzigd.)

In principe is het gewoon een soort redirect, toch is het raadzaam om gewoon het protocol te hanteren, het sturen van berichten zonder de header

  • GUARD
  • DAEMON_MESSAGE_CUSTOM

zal tot conflicten leiden, omdat de client applicatie niet in staat is om onderscheid te maken tussen verschillende berichten.


Architectuur

De Fontys VR-Cave daemon is een multithreaded applicatie. Er wordt gebruik gemaakt van meerdere drivers (momenteel FOB en WiiYourself).

Voor deze drivers zijn wrappers geschreven:

  • fob (fob.h/fob.cpp)
  • wii (wii.h/wii.cpp)

Beide wrappers hebben de volgende eigenschappen:

  • Alle functies zijn thread-safe.
  • Initialisaties/de-initialisaties zijn beveiligd.
  • Alle functies termineren uit zichzelf

De thread-safe eigenschap wordt gewaarborgd door het gebruik van een mutex voor iedere wrapper. Het is niet mogelijk dat meerdere functies van een wrapper tegelijk actief zijn.

Met initialisatie/de-initialisatie beveiliging wordt er gedoeld op dat het niet mogelijk is om een driver twee keer te initialiseren. (Indien de driver al geïnitialiseerd is, dan wordt de volgende initialisatie aanroep gewoonweg genegeerd.) Een dubbele de-initialisatie heeft ook geen effect. Het aanroepen van driver-functies terwijl de driver niet geïnitialiseerd is heeft geen fatale effecten. De laatste eigenschap (terminatie) garandeert dat er geen deadlocks kunnen ontstaan.

Dergelijke interfaces zijn vereist om te kunnen controleren of de applicatie correct functioneert. De daemon heeft drie threads, waarvan er twee echt zichtbaar zijn binnen de applicatie.

Op de hoofdthread draait de window loop. Deze window loop roept de functies update(uint msec) en render() aan (zie daemon.cpp). Deze functies worden aangeroepen indien de daemon window focus heeft.

Voordat dit alles gebeurt, wordt er eest een aanroep naar init() gedaan. Bij afsluiting wordt deinit() aangeroepen.

Tijdens de initialsatie (via init()), wordt er een thread gestart die continue data broadcast. Tevens is er een functie onRecieve() die aangeroepen wordt indien er berichten binnen komen (deze worden geforward). De main thread, de broadcast thread, en de onRecieve() thread zijn verschillend.

De synchronisatie ervan is echter goed te overzien, mede dankzij het isolatie niveau van de wrappers. Een voorbeeld: de wii en fob data wordt gesampled in de broadcast thread. Tevens is deze data benodigd in de main thread (om het te visualiseren). Een mutex binnen de wrapper zorgt dat het goedkomt. Ook kunnen deadlocks zich niet voordoen omdat iedere functie onafhankelijk termineert.

Indien er nieuwe drivers toegevoegd worden, dan is het zeer wenselijk dat de wrappers de eigenschappen hebben die hierboven zijn genoemd.


UT2004 hack

Dit betreft de Fontys Cave port van het spel UT2004. In de daemon is een "hack" aangebracht. (In beginsel is dit helemaal niet wenselijk.) UT2004 script heeft de mogelijkheid TCP sockets uit te lezen, evenals UDP sockets. Er zitten functies bij om dit te bewerkstelligen. De binaire data uitlees functie is alleen zo geconstrueerd, dat er alleen pakketjes van minder dan 255 bytes uitgelezen kunnen worden. Op de wiki van UTScript staat vaag een workaround beschreven, maar het ziet er niet naar uit dat deze werkt.

De UDP pakket naar tekst-string functie werkt echter wel. Het is alleen niet duidelijk hoe strings in UTscript worden gerepresenteerd en of het mogelijk is deze weer terug te converteren naar binaire data. Om deze rede verstuurt de daemon een UDP pakket met tekstuele data naar poort 8882.

Ongeacht de UDP functies binnen UT2004, is deze aanpak waarschijnlijk toch vereist. UT2004 haalt data binnen via UTscript en via de OpenGL driver. Beide processen zouden dan van dezelfde UDP poort moeten lezen. Dit is iets wat waarschijnlijk niet gaat werken.

Zie ook Unreal Tournament 2004.






































.


































.