Extend UFO@home with modules
There is no account: a module is a file at an address of yours. The editor loads it, the recordings that need it name it.
What a module brings#
Sources of data (aerial imagery, ground relief, weather, air traffic…) and 3D models, each with the credit its licence requires. They join the built-in ones: in the editor's pickers after them, and in the scene wherever a recording names one of its models.
The manifest#
A JSON file, ufoathome-module.json by convention, readable from other sites (CORS). Its relative addresses are read from its own.
{
"format": 1,
"id": "acme",
"name": "ACME surveys",
"credit": "© ACME",
"creditUrl": "https://acme.example",
"contributes": {
"imagery": [{
"id": "acme-ortho", "name": "ACME ortho", "credit": "© ACME", "creditUrl": "https://acme.example/licence",
"tiles": "https://tiles.acme.example/ortho/{z}/{x}/{y}.jpg"
}],
"elevation": [{
"id": "acme-relief", "name": "ACME relief", "credit": "© ACME", "creditUrl": "https://acme.example/licence",
"tiles": "relief/{z}/{x}/{y}.png"
}],
"models": [{
"id": "acme-weather-balloon", "kind": "entity", "name": "Weather balloon",
"file": "models/balloon.glb", "sizeM": { "widthM": 2, "lengthM": 2, "heightM": 2 },
"credit": { "title": "Weather balloon", "author": "ACME", "license": "CC BY 4.0", "sourceUrl": "https://acme.example/models" }
}]
},
"script": "module.js"
}
imagery: tiles of ordinary images,{z},{x}and{y}in the address in whatever order the service has them.elevation: tiles of relief encoded as Terrarium PNG (height = R × 256 + G + B / 256 − 32768 m).models: glTF or GLB files, each with its kind (vehicle,aircraft,building,entity…), its size and its credit, as in UFO@home's own catalogue.- An
idanother module or UFO@home already gives at the same place is refused, for the whole module: give yours a prefix of your own.
When it needs code#
A service whose answers have a shape of their own needs code: script names an ES module whose default export returns implementations of the interfaces of src/modules.
// module.js: its default export returns more implementations, at any point.
export default ({ manifestUrl }) => ({
weather: [{
id: "acme-weather", name: "ACME weather", credit: "© ACME", creditUrl: "https://acme.example",
create: () => ({ /* the methods of WeatherProvider */ })
}]
})
The places a module can add to: weather, place, timeZone, narrative, assessment, lightPollution, aircraft, upperAir, elevation, imagery, surroundings, decorModels, skyEvents, interpretations, perception.
A script runs with everything the page can do. It is run only for a module the person added themself, never for one a recording names.
Simulating a kind of event of the sky#
A kind of event of the sky (lightning, a rocket, a flare…) is what a recording states under skyEvents.<key>, drawn in the scene and edited in a part of the Sky group of its own. Its simulation gives the scene a three.js object and places it at every tick, from the instant and the frame the scene is in (where the eye is, the air, the ground, the wind); it never draws itself. The frame also says where the observer really stands, the real instant, the faintest magnitude seen, what the clouds and the air let through, and lets it light the ground (lightFrom) and dazzle the eye (glareFrom). Build it with the three the script is given, never a copy of your own. The launches, the beams and the fireballs of UFO@home are written this way, in src/modules/sky-events.
// A kind of event of the sky: stated in recordings under skyEvents.ballLightning.
export default ({ three }) => ({
skyEvents: [{
id: "ballLightning", name: "Ball lightning", credit: "© ACME", creditUrl: "https://acme.example",
create: () => ({
key: "ballLightning",
title: { en: "Ball lightning", fr: "Foudre en boule" },
simulation: async () => () => {
const object = new three.Group()
return {
object,
drawnCount: 0,
// Places and lights the events at t (ms), from frame.eye; draws nothing itself.
update(events, t, frame) { /* … */ },
dispose() { object.removeFromParent() }
}
},
// Optional: the part of the Sky group that edits them.
editor: async () => (container, host, language) => ({ sync() {}, refresh() {}, setTime(t) {}, async setLanguage(l) {} })
})
}]
})
A kind of interpretation#
A reading of an account that a few parameters state, computed into bodies when shown (sky lanterns let go from a garden, decoy flares an aircraft dispensed): stated under interpretation.<key>, given to interpretations with a key, a title, bodiesOf(item) returning the bodies it is, and an optional editor. Not a fact of the sky: what somebody claims the account saw.
Adding one in the editor#
In the Files group, the Modules item: type the manifest's address and press Add. Your modules are kept in this browser, can be turned off or removed, and saved to a file (Save my modules) that loads them elsewhere (Load modules).
A recording that needs one#
A recording that draws a model of a module names it, so that whoever opens it sees the same thing: the editor writes its address in the file as soon as one of its models is used, relative to the recording when both are on the same site, and Named by this recording unticks it. A reader who did not add the module gets what its manifest states, without its script.
{
"modules": ["https://acme.example/ufo/ufoathome-module.json"]
}
Into UFO@home itself#
An implementation can also join UFO@home: each kind has its folder under src/modules, whose README says what to write and where to list it. A pull request is welcome.