sons with new assets

master
Fabien Benetou 2 years ago
parent a0f4e9a1ce
commit 6c599de5a1
  1. 689
      index.html

@ -4,116 +4,296 @@
</head> </head>
<body> <body>
<script> <script>
/*
play sounds as described in email
Sounds/FX/CEREMA_BOUCLE_EAU.mp3
try rely on periodic bounding box height as volume
unfortunately document.querySelector("#watersim").object3D.children[0].geometry.boundingBox.max or min doesn't seem to change despite the displacement map working
*/
AFRAME.registerComponent('randomfx', {
init: function () {
var FXsounds = Array.from( document.querySelectorAll(".randomfx") )
setInterval( _ => { FXsounds[Math.floor(Math.random()*FXsounds.length)].components.sound.playSound() }, 9000 )
}
})
// configurator.html e.g https://github.com/Utopiah/philosophy-vr-experimentation-lab/blob/master/public/configurator.html
/* TODO 11th
Repare
- Il faudrait peut être faire un léger shift vers le bas pour que l'on ne voit pas la texture de l'eau initialement. (le 0 de la simulation vaut 0.001 dans les données)
- Le cube reset est bas et petit en VR
- Avec le bug de reset sur l'eau, est ce qu'il serait possible d'associer un des boutons des manettes du Quest à l'action "Recharger la page" ? Comme la simulation est courte, cela permettrait de rafraîchir facilement.
Extra
- Couleurs batiments/toits plus neutres
Problematique
- Au niveau du sol, en début de simulation, on a un effet de scintillement entre le sol blanc et l'eau qui peut être désagréable.
- J'ai un utilisateur qui m'a demandé si c'était possible d'animer un peu la texture de l'eau
A discuter
- Il y a un bug avec la position du modèle 3D et des bâtiments procéduraux (au moins sur la Ruelle) cf capture
Etonnant car arbres OK?
A reparer
- faire le test in_situ quand la localisation sera gere
- Il faut un peu revoir les hot-spots dans le quartier des Baquets, certains sont dans des jardins et on peut se retrouver enferme
*/
// TODO after pres 26th :
/*
- fix reset button (water animation ignored)
- add lamp model (insure it doesn't load twice)
- add "dirty" water texture with animation (via e.g repeat/offset in material)
- define color scheme for specific objects, e.g house/roof
- fix teleports, should include parent offsets
- mapping type/tags/filters -> function + opt. colors scheme or 3D model
- geom
var houses = josm.elements.filter( e => (e.type == "way" && e.tags && e.tags["building"]) )
.filter( i => !loadedosmways.includes(i.id) )
.map( e => e.geometry)
houses.map( e => { makeBuilding( e, buildingHeight*(1+Math.random()), el ) })
highway
- points
var trees = josm.elements.filter( i => i.type == "node" && i.tags && i.tags.natural == "tree")
var lamp = josm.elements.filter( i => i.type == "node" && i.tags && i.tags.highway == "street_lamp")
trees.map( e => { makeTree( e, el ) } )
lamp.map( e => { makeLamp( e, el ) } )
*/
// TODO pres 19th :
// - showcase elevations unoptimized and possibly optimized with FPS, ideally on Quest2
// - showcase new assets from OSM (to check first, might look at recent add in that area)
// Done
// - add models of 2nd spot and easily switch from one to the other, e.g URL argument
// - add 1 audio track (might have to check on phones if fine or if need extra permission
// - transform function resetAnimations() to interactive action, possibly with seeking
const simsrc = "ruelle_sim_data/crop_outpy_"
const simext = ".jpg"
const simstart = 1
const simend = 95
const sim_filename_padding = true
const animationSpeedStep = 500
var animationCurrentStep = simstart
var wsimanim
function animationStep(){
wsim.setAttribute("material", {
// offset: {x: animationCurrentStep/1000, y:0}, // impacts also the displacement map, not just the texture
displacementMap: simsrc+(animationCurrentStep++)+simext,
})
//wsim.setAttribute("material", "displacementMap", simsrc+(animationCurrentStep++)+simext)
if (animationCurrentStep >= simend) resetAnimations()
}
var currentGeoPose = null; // https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition var currentGeoPose = null; // https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition
navigator.geolocation.getCurrentPosition( geolocalized ) // should handle error, including being ignored // navigator.geolocation.getCurrentPosition( geolocalized ) // should handle error, including being ignored
// disable for faster testing
// to consider https://github.com/mourner/suncalc
// otherwise have 2 fallbacks via menu // otherwise have 2 fallbacks via menu
var positionsCurated = [ var positionsCurated = [
[50.8440239,4.3952614], // brussels for tests // [50.8354400, 4.3582900],
[1,1] { position:[50.8365354, 4.3581719], hotspots: []},
{ shortname: "baquets", worldposition:[83.03582, -346.4272], position:[49.3516400, 0.5461600],
hotspots: [
// done via the AFrame inspector then pasting here, then regex
[-126.65372, -0.46424], [-126.65372, -0.46424], [-126.29815, -26.94149], [-125.92851, -42.42632], [-122.30186, -52.18768], [-105.27281, -51.22971], [-104.02136, -36.467], [-102.82045, -18.61413], [-102.20349, -3.76295], [-118.85539, 2.56914],
],
hotspotoffsetPosition: new THREE.Vector3(175.80343,0,-286.43518),
//hotspotoffsetRotation: "0 8.047765190407544 0"
},
{ shortname: "ruelle", worldposition:[-126, -40], position:[49.3605700, 0.5031000],
hotspots: [
[-134.62038, -13.29107], [-125.89544, -30.36393], [-119.72245, -49.78609], [-96.91944, -95.00968], [-90.17903, -109.1628],
, [-142.76086, 11.1124]
],
hotspotoffsetPosition: new THREE.Vector3(),
//hotspotoffsetPosition: "0 0 0",
//hotspotoffsetRotation: "0 0 0",
},
{ position:[50.8440239, 4.3952614], hotspots: []}, // brussels for tests
] ]
// should get in turn nwr(50.844,4.395,50.845,4.396); as example of scale var textureOffsetStart = 0
var textureOffsetEnd = 1
AFRAME.registerComponent('texture-drift', {
// should be able to animate instead... but somehow didn't manage.
// animation_texture="property: components.material.material.map.offset.x; from: 0; to: 1; dur: 2000; easing: linear; loop: true; dir: alternate;"
init: function () {
var el = this.el
el.addEventListener('materialtextureloaded', function () {
console.log(el,"text loaded")
setTimeout(function () { // setTimeout for good measure.
var x = el.components.material.material.map.offset.x
setInterval( _ => {
(x >= textureOffsetEnd) ? x = textureOffsetStart : x += .0001
}, 100)
}, 200);
});
}
})
AFRAME.registerComponent('set-initial-camera-from-data', {
init: function () {
this.el.setAttribute("position", positionsCurated[selection].worldposition[0] + " 1.6 " + positionsCurated[selection].worldposition[1] )
}
})
var ignoreCache = true // should be joined as 1 or better be location dependent
const disableWaysForGLTFExportInHubs = false
// default position fallback to faciliate tests var selection = 2 // default position fallback to faciliate tests
var position = positionsCurated[0]
// initially done as a different page at first with query parameter // initially done as a different page at first with query parameter
var forcePosition = AFRAME.utils.getUrlParameter('position'); var forceSelection = AFRAME.utils.getUrlParameter('position');
if ( forcePosition ) position = positionsCurated[forcePosition] if ( forceSelection ) selection = forceSelection
var position = positionsCurated[selection].position
var hotspots = positionsCurated[selection].hotspots.map( i => [ i[0]+positionsCurated[selection].hotspotoffsetPosition.x ,i[1]+positionsCurated[selection].hotspotoffsetPosition.z ] )
var precision = 4 var precision = 4
var bbox_limit = 10/10**precision var bbox_limit = 10/10**precision
bbox_limit = 3*10/10**precision
var elevation_res = 10 var elevation_res = 10
// generate 3D model from the target location from OSM data // generate 3D model from the target location from OSM data
var bbox = "" + position[0] var bbox = "" + (position[0]-bbox_limit/2)
+ "," + position[1] + "," + (position[1]-bbox_limit/2)
+ "," + (position[0]+bbox_limit) + "," + (position[0]+bbox_limit-bbox_limit/2)
+ "," + (position[1]+bbox_limit) + "," + (position[1]+bbox_limit-bbox_limit/2)
var queryOSM = `https://overpass-api.de/api/interpreter?data=[out:json];nwr(${bbox});out;` var queryOSM = `https://overpass-api.de/api/interpreter?data=[out:json];nwr(${bbox});out;`
// format https://overpass-api.de/output_formats.html#json // format https://overpass-api.de/output_formats.html#json
// bounding box https://dev.overpass-api.de/overpass-doc/en/full_data/bbox.html // bounding box https://dev.overpass-api.de/overpass-doc/en/full_data/bbox.html
// testing https://overpass-turbo.eu // testing https://overpass-turbo.eu
fetch(queryOSM).then(response => response.json()).then(data => fromOSMTo3DElements(data) )
var queryOSMgeom = `https://overpass-api.de/api/interpreter?data=[out:json];nwr(${bbox});out geom;`
// fetch flood data of target location (should be only 2 available) as timeseries // fetch flood data of target location (should be only 2 available) as timeseries
var queryCEREMA = "https://cerema.fr/api/"+position
//fetch(queryCEREMA).then(response => response.json()).then(data => console.log(data) ) var locations = []
for (var i=0;i<elevation_res;i++)
for (var j=0;j<elevation_res;j++)
locations.push({
latitude:Number( (position[0]+(bbox_limit/elevation_res)*i).toFixed(precision) ),
longitude:Number( (position[1]+(bbox_limit/elevation_res)*j).toFixed(precision) ),
})
var queryElevation = 'https://api.open-elevation.com/api/v1/lookup'
// doesn't seem to have sufficient resolution, might use IGN instead
// animate water based on flood data // animate water based on flood data
// done as animation on blue plane
// see simulation_data_example.json
// allow viewer to control the animation e.g restart, pause, change time // allow viewer to control the animation e.g restart, pause, change time
// seek on the set of animations
// modify environment to reflect time of day // modify environment to reflect time of day
// done as animation on point light
AFRAME.registerComponent('osm', { // currently only 1 hand, the right one var buildingHeight = 6
init: function () { var forceHeight = AFRAME.utils.getUrlParameter('height');
} if ( forceHeight ) buildingHeight = Number( forceHeight )
}); var wayHeight = .5
var globaljosm var globaljosm
var offsets = { // used to re-center the map
"x": Number( position[0].toFixed(precision-1) ),
"y": 0.1, // try to avoid z-fighting
"z": Number( position[1].toFixed(precision-1) )}
var scale = 10**(precision+1)
function pointFromCoordinates( p ){
return new THREE.Vector3( (p.lat-offsets.x)*scale, 0, (p.lon-offsets.z)*scale )
}
function rescale( value ){ function curvesTo3DElements( curves, el ){
return (((value*10**precision)-(Math.round(value*10**precision)))*10).toFixed(precision) curves.map( c => {
makeLine( c.geometry.coordinates.map( p => pointFromCoordinates({lat:p[1],lon:p[0]}) ) , c.properties.ALTITUDE, el )
})
} }
function elevationTo3DElements( locations ){ function elevationTo3DElements( elevations, el ){
console.log( locations ) localStorage.setItem('elevation', JSON.stringify( elevations ) )
locations.map( h => { var minElevation = elevations.sort( (a,b) => a.elevation - b.elevation )[0].elevation
var el = document.createElement("a-sphere") elevations.map( h => {
el.setAttribute("color", "blue") var pointEl = document.createElement("a-sphere")
el.setAttribute("radius", "0.1") pointEl.setAttribute("color", "blue")
pointEl.setAttribute("radius", "0.1")
// simplification, for a proper polygon see make3DFloor() but here shouldn't assume a centroid // simplification, for a proper polygon see make3DFloor() but here shouldn't assume a centroid
// could instead rely on a grid // could instead rpointEly on a grid
el.setAttribute("position", h.latitude + " "+ h.elevation +" " + h.longitude ) var pos = pointFromCoordinates( {lat:h.latitude,lon:h.longitude} )
// rescale() does not work here, probably different format than in addHouses() pos.y = h.elevation - minElevation
pointEl.setAttribute("position", AFRAME.utils.coordinates.stringify( pos ) )
// see pointFromCoordinates
// could find the minimum altitude then remove it from every other values // could find the minimum altitude then remove it from every other values
AFRAME.scenes[0].appendChild( el ) el.appendChild( pointEl )
}) })
} }
function getElevation(){ function fromOSMTo3DElements( josm, el ){
var locations = [] localStorage.setItem('osm', JSON.stringify( josm ) )
for (var i=0;i<elevation_res;i++) globaljosm = josm // used for debug in console
for (var j=0;j<elevation_res;j++)
locations.push({ // right click on elements on https://overpass-turbo.eu/s/1jbc allows to see type visually
latitude:Number( (position[0]+(bbox_limit/elevation_res)*i).toFixed(precision) ), //var houses = josm.elements.filter( e => (e.type == "way" && e.tags && e.tags["add:housenumber"]) ).map( e => e.geometry)
longitude:Number( (position[1]+(bbox_limit/elevation_res)*j).toFixed(precision) ), var houses = josm.elements.filter( e => (e.type == "way" && e.tags && e.tags["building"]) )
}) .filter( i => !loadedosmways.includes(i.id) )
fetch('https://api.open-elevation.com/api/v1/lookup', { .map( e => e.geometry)
method: 'POST', houses.map( e => { makeBuilding( e, buildingHeight*(1+Math.random()), el ) })
headers: { idfound = josm.elements.filter( e => (e.type == "way" && e.tags && e.tags["building"]) )
'Accept': 'application/json', idfoundfilter = josm.elements.filter( e => (e.type == "way" && e.tags && e.tags["building"]) )
'Content-Type': 'application/json', .filter( i => !loadedosmways.includes(i.id) )
}, console.log("way IDs found", idfound)
body: JSON.stringify({locations:locations}) console.log("way IDs filtered", idfoundfilter)
}).then(response => response.json()).then(data => elevationTo3DElements(data.results) )
} // to add trees
var trees = josm.elements.filter( i => i.type == "node" && i.tags && i.tags.natural == "tree")
var lamp = josm.elements.filter( i => i.type == "node" && i.tags && i.tags.highway == "street_lamp")
function fromOSMTo3DElements( josm ){ // https://www.openstreetmap.org/node/9923232483
globaljosm = josm
var ways = josm.elements.filter( e => (e.type == "way") ) trees.map( e => { makeTree( e, el ) } )
var houses = josm.elements.filter( e => (e.type == "node" && e.tags && e.tags["addr:housenumber"])) lamp.map( e => { makeLamp( e, el ) } )
// does not give a polygon, might need further requests. Might also not be the right way.
addHouses( houses ) /* consider also
} landuse forest e.g https://www.openstreetmap.org/way/42198660
waterway river e.g https://www.openstreetmap.org/way/112050282
function addHouses( houses ){ or rather with type multipolygon https://www.openstreetmap.org/relation/8029633
houses.map( h => { */
var el = document.createElement("a-box")
// simplification, for a proper polygon see addWall() and make3DFloor() // console.log('comparing ways from OSM to glTF')
el.setAttribute("position", rescale( h.lat ) + " 0 " + rescale( h.lon )) // existing_houses = josm.elements.filter( e => (e.type == "way" && e.tags && e.tags["building"]) )
AFRAME.scenes[0].appendChild( el ) // .filter( i => loadedosmways.includes(i.id) )
}) // .map( e => e.geometry)
// existing_houses.map( e => { makeBuilding( e, buildingHeight*(.5), el ) })
// used for alignment
//var highways = josm.elements.filter( e => (e.type == "way" && e.tags["highway"]) ).map( e => e.geometry)
var highways = josm.elements.filter( e => (e.type == "way" && e.tags && e.tags["highway"]) ).map( e => e.geometry)
// not so good, shows paths rather than a usable geometry
if (!disableWaysForGLTFExportInHubs)
highways.map( e => { makeWay( e, wayHeight, el ) })
} }
function geolocalized( pos ){ function geolocalized( pos ){
console.log( pos.coords ) console.log( pos.coords )
} }
function addWall(a, b, h, el){ function addWall(a, b, h, el, color=0x6d6a64){
var geometry = new THREE.BufferGeometry(); var geometry = new THREE.BufferGeometry();
// create a simple rectangle shape. We duplicate the top left and bottom right // create a simple rectangle shape. We duplicate the top left and bottom right
// vertices because each vertex needs to appear once per triangle. // vertices because each vertex needs to appear once per triangle.
@ -129,32 +309,33 @@ function addWall(a, b, h, el){
// itemSize = 3 because there are 3 values (components) per vertex // itemSize = 3 because there are 3 values (components) per vertex
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
var material = new THREE.MeshBasicMaterial( { color: 0xffffff, opacity: 0.5, transparent: true, side: THREE.DoubleSide } ); var material = new THREE.MeshBasicMaterial( { color: color, side: THREE.DoubleSide } );
var mesh = new THREE.Mesh( geometry, material ); var mesh = new THREE.Mesh( geometry, material );
var entity = document.createElement("a-entity"); var entity = document.createElement("a-entity");
// not sure it's the best level of abstraction, maybe building instead
entity.object3D.add( mesh ); entity.object3D.add( mesh );
el.appendChild( entity ); el.appendChild( entity );
} }
function addFloorFan(a, b, c, el){ function addFan(a, b, c, el, verticalOffset, color ){
var geometry = new THREE.BufferGeometry(); var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array( [ var vertices = new Float32Array( [
a.x, a.y, a.z, a.x, a.y+verticalOffset, a.z,
b.x, b.y, b.z, b.x, b.y+verticalOffset, b.z,
c.x, c.y, c.z, c.x, c.y+verticalOffset, c.z,
] ); ] );
// itemSize = 3 because there are 3 values (components) per vertex // itemSize = 3 because there are 3 values (components) per vertex
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
var material = new THREE.MeshBasicMaterial( { color: 0xffffff, opacity: 0.5, transparent: true, side: THREE.DoubleSide } ); var material = new THREE.MeshBasicMaterial( { color: color, side: THREE.DoubleSide } );
var mesh = new THREE.Mesh( geometry, material ); var mesh = new THREE.Mesh( geometry, material );
var entity = document.createElement("a-entity"); var entity = document.createElement("a-entity");
entity.object3D.add( mesh ); entity.object3D.add( mesh );
el.appendChild( entity ); el.appendChild( entity );
} }
function make3DFloor(points, el){ // assumes an array of threejs Vector3, not AFRAME elements anymore function make3DFloor(points, el, color=0xedeceb){
var entity = document.createElement("a-entity") var entity = document.createElement("a-entity")
var centroid = getCentroid( points ) var centroid = getCentroid( points )
entity.id = "roomcentroid" entity.id = "roomcentroid"
@ -162,7 +343,21 @@ function make3DFloor(points, el){ // assumes an array of threejs Vector3, not AF
el.appendChild( entity ) el.appendChild( entity )
points.map( (e,i,arr) => { points.map( (e,i,arr) => {
(i==arr.length-1) ? addFloorFan(arr[0], e, centroid, el) : addFloorFan(e, arr[i+1], centroid, el) (i==arr.length-1) ? addFan(arr[0], e, centroid, el, 0, color) : addFan(e, arr[i+1], centroid, el, 0, color)
})
}
function make3DRoof(points, el , height, color=0x29465b){
var entity = document.createElement("a-entity")
var centroid = getCentroid( points )
centroid.y += 3 // pointy roof
entity.id = "roomcentroid"
entity.setAttribute("position", centroid)
el.appendChild( entity )
points.map( (e,i,arr) => {
(i==arr.length-1) ? addFan(arr[0], e, centroid, el, height, color) : addFan(e, arr[i+1], centroid, el, height, color)
}) })
} }
@ -177,48 +372,340 @@ function getCentroid(points){
return centroid; return centroid;
} }
function makeWay( points, height, el ){
var poly = points.map( p => pointFromCoordinates( p ) )
var way = document.createElement("a-entity")
//way.setAttribute("position", "0 1 0") // vertical offset to avoid z-fighting on large horizontal planes
way.className = "way"
way.setAttribute("shadow", "") // somehow doesn't seem to receive, only casts
el.appendChild( way )
make3DFloor( poly, way )
}
function makeLamp( treedata, el){
// <a-gltf-model position="-2 0 -6" scale="2 2 2" src="arbre.gltf"></a-gltf-model>
var tree = document.createElement("a-entity")
tree.setAttribute("position", AFRAME.utils.coordinates.stringify(
pointFromCoordinates( {lat:treedata.lat, lon:treedata.lon} )
) )
tree.setAttribute("gltf-model", "Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Ruelle_Lampadaire.glb" )
el.appendChild( tree )
}
function makeTree( treedata, el){
// <a-gltf-model position="-2 0 -6" scale="2 2 2" src="arbre.gltf"></a-gltf-model>
var tree = document.createElement("a-entity")
tree.setAttribute("position", AFRAME.utils.coordinates.stringify(
pointFromCoordinates( {lat:treedata.lat, lon:treedata.lon} )
) )
tree.setAttribute("gltf-model", "Visuals/EXPORTS/EXPORTS_gltf/AA_Ruelle_Arbre.glb" )
el.appendChild( tree )
}
function makeBuilding( points, height, el ){
// somehow there is an angle, a slanting. Building usually have right angles at corners, not here.
var poly = points.map( p => pointFromCoordinates( p ) )
var building = document.createElement("a-entity")
building.className = "building"
building.setAttribute("position", "0 "+ Math.random()/10 + " 0") // could rely on elevation instead.
//Pavement would need to be high enough to make the connection between that and street.
//which might make it look pointless
el.appendChild( building )
makeWalls( poly, height, building )
// make3DFloor( poly, building ) // not visibile so might be removed
var color = new THREE.Color( 0x8080FF ).offsetHSL(0,0,-Math.random());
make3DRoof( poly, building, height, color )
building.setAttribute("shadow", "")// somehow doesn't seem to receive it, only casts
/*
var inbuilding = document.createElement("a-entity")
inbuilding.className = "inbuilding"
building.appendChild( inbuilding )
makeWalls( poly, 0.2, inbuilding )
make3DFloor( poly, inbuilding )
inbuilding.setAttribute("scale", "1.1 1 1.1") // adds an horizontal offset instead of being centered on building
inbuilding.setAttribute("position", "-.2 0 -.2")
*/
}
function makeLine( poly, h, el ){
var color = 0x6d6a64 //+Math.floor( 100*Math.random() )
poly.map( (e,i,arr) => { if (i<arr.length-1) addWall(e, arr[i+1], h, el, color) })
}
function makeWalls( poly, h, el ){
var color = new THREE.Color( 0x808080 ).offsetHSL(0,0,-Math.random()/10);
poly.map( (e,i,arr) => { (i==arr.length-1) ? addWall(arr[0], e, h, el, color) : addWall(e, arr[i+1], h, el, color) })
}
var elevationDataFromCache = localStorage.getItem('elevation');
// includes request date so might prefer to invalidate after a while
var osmDataFromCache = localStorage.getItem('osm');
if (ignoreCache){
elevationDataFromCache = null
osmDataFromCache = null
}
AFRAME.registerComponent('elevation', { AFRAME.registerComponent('elevation', {
init: function () { init: function () {
var el = this.el var el = this.el
getElevation() if (!elevationDataFromCache){
console.log("no elevation data from cache, querying")
fetch(queryElevation, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({locations:locations})
}).then(response => response.json()).then(data => elevationTo3DElements(data.results, el ) )
} else {
console.log("elevation data from cache")
elevationTo3DElements( JSON.parse( elevationDataFromCache ), el )
}
}
})
AFRAME.registerComponent('courbes', {
init: function () {
var el = this.el
console.log("elevation data from ign courbes.json file")
fetch("courbe_clipped.json" ).then(response => response.json()).then(data => curvesTo3DElements(data.features, el ) )
} }
}) })
AFRAME.registerComponent('procgen', {
AFRAME.registerComponent('osm', {
init: function () {
var el = this.el
if (!osmDataFromCache){
console.log("no osm data from cache, querying")
fetch(queryOSMgeom).then(response => response.json()).then(data => fromOSMTo3DElements( data, el ) )
} else {
console.log("osm data from cache")
fromOSMTo3DElements( JSON.parse( osmDataFromCache ), el )
}
}
})
// includes request date so might prefer to invalidate after a while
AFRAME.registerComponent('cerema', {
init: function () {
var el = this.el
wsim = el
animationCurrentStep = simstart
wsimanim = setInterval(animationStep, animationSpeedStep)
}
})
AFRAME.registerComponent('hotspots', {
init: function () { init: function () {
var el = this.el var el = this.el
var a = new THREE.Vector3(1,0,1) hotspots.map( h => {
var b = new THREE.Vector3(-1,0,1) var pointEl = document.createElement("a-sphere")
var c = new THREE.Vector3(-1,0,-1) pointEl.setAttribute("opacity", .3)
var d = new THREE.Vector3(0,0,-2) pointEl.setAttribute("geometry", "segmentsHeight: 4; segmentsWidth: 8")
var e = new THREE.Vector3(1,0,-1) pointEl.setAttribute("position", h[0] + " 3 " + h[1])
var poly = [a,b,c,d,e] pointEl.setAttribute("teleport-via-cursor", true)
var h = 2 el.appendChild( pointEl )
poly.map( (e,i,arr) => { (i==arr.length-1) ? addWall(arr[0], e, h, el) : addWall(e, arr[i+1], h, el) }) })
make3DFloor( poly, el )
}
})
AFRAME.registerComponent('teleport-via-cursor', {
init: function () {
var el = this.el
var cam = document.querySelector("#camera-rig")
this.el.addEventListener('click', function (evt) {
var pos = el.getAttribute("position")
cam.setAttribute("position", pos.x + " 1.6 " + pos.z)
// might want to blink too, e.g black sphere around the camera for .5s
});
this.el.addEventListener('fusing', function (evt) {
el.setAttribute("opacity", 1)
});
this.el.addEventListener('mouseleave', function (evt) {
el.setAttribute("opacity", 0.3)
});
}
});
AFRAME.registerComponent('hud', {
init: function () {
var el = this.el
var ringEl = document.createElement("a-ring")
ringEl.setAttribute("theta-length", "0")
ringEl.setAttribute("position", "1.5 0 -3")
el.appendChild( ringEl )
var pointEl = document.createElement("a-text")
pointEl.setAttribute("value", "0.0")
pointEl.setAttribute("position", "0.3 0 -0.9")
el.appendChild( pointEl )
this.ring = ringEl
this.hud = pointEl
},
tick: function (time, timeDelta) {
animationTime++
if (animationTime > 20000) {
this.start = 0
animationTime = 0
}
this.hud.setAttribute("value", animationTime)
this.ring.setAttribute("theta-length", animationTime/20000*360)
}
})
AFRAME.registerComponent('listosmways', {
init: function () {
var el = this.el
this.el.addEventListener('model-loaded', function (evt) {
//el.object3D.traverse( i => { if (i.name && i.name !="Scene" && i.type=="Group") loadedosmways.push(Number( i.name )) })
el.object3D.traverse( i => { if (i.name && i.name !="Scene" && i.type=="Mesh") loadedosmways.push(Number( i.name )) })
});
}
})
AFRAME.registerComponent('resetanimations', {
init: function () {
var el = this.el
this.el.addEventListener('click', function (evt) {
resetAnimations()
});
this.el.addEventListener('fusing', function (evt) {
el.setAttribute("opacity", 0.3)
});
this.el.addEventListener('mouseleave', function (evt) {
el.setAttribute("opacity", 1)
});
} }
}); });
AFRAME.registerComponent('displaymodels', {
init: function () {
// sufficient, removed the others otherwise causes raycaster bugs
Array.from( document.querySelectorAll('[class*="models_"]') ).map( m => {
m.setAttribute( "scale", ".01 .01 .01" )
})
Array.from( document.querySelectorAll(".models_"+positionsCurated[selection].shortname) ).map( m => {
m.setAttribute( "scale", "1.2 1.2 1.2")
m.setAttribute( "visible", "true" )
m.setAttribute( "listosmways", "" )
})
}
})
function resetAnimations(){
Array.from( document.querySelectorAll("[animation]") )
.map( a => Object.keys( a.components )
.filter( k => k.match("animation") )
.map( key => a.components[key].time = 0 )
// could also use play() or pause()
)
animationTime = 0
animationCurrentStep = simstart
clearInterval(wsimanim)
wsimanim = setInterval(animationStep, animationSpeedStep)
}
var animationTime = 0 // this isn't exactly aligned with animations
// because virtual and real time hasn't be decided yet (waiting on simulation results)
// virtual time would amount to 2 to 3min total.
loadedosmways = [];
function playSound(){
var entity = document.querySelector('[sound]');
entity.components.sound.playSound();
}
</script> </script>
<a-scene osm elevation> <button style="z-index: 1; position: absolute;" onclick="playSound()">Play sound</button>
<a-scene NOcourbes displaymodels stats>
<a-entity id="hotspots" hotspots ></a-entity>
<!-- Quest available only during event, otherwise cardboard --> <!-- Quest available only during event, otherwise cardboard -->
<a-entity id="leftHand" hand-tracking-controls="hand: left;"></a-entity> <a-entity id="leftHand" hand-tracking-controls="hand: left;"></a-entity>
<a-entity id="rightHand" hand-tracking-controls="hand: right;"></a-entity> <a-entity id="rightHand" hand-tracking-controls="hand: right;"></a-entity>
<!-- for cardboard consider instead https://aframe.io/docs/1.3.0/components/cursor.html --> <a-entity id="camera-rig" set-initial-camera-from-data>
<a-entity camera look-controls hud>
<a-entity procgen position="-4 0 -4"></a-entity> <a-sound autoplay="false" loop="true" src="src: url(Sounds/AMBIANCES/CEREMA_AMB_VILLE.mp3)"></a-sound>
<a-sound class="randomfx" autoplay="false" loop="false" src="src: url(Sounds/FX/CEREMA_CRACK_MAISON_01.mp3)"></a-sound>
<a-sound class="randomfx" autoplay="false" loop="false" src="src: url(Sounds/FX/CEREMA_CRACK_MAISON_02.mp3)"></a-sound>
<a-sound class="randomfx" autoplay="false" loop="false" src="src: url(Sounds/FX/CEREMA_CRACK_MAISON_03.mp3)"></a-sound>
<a-sound class="randomfx" autoplay="false" loop="false" src="src: url(Sounds/FX/CEREMA_CRACK_MAISON_04.mp3)"></a-sound>
<a-sound class="randomfx" autoplay="false" loop="false" src="src: url(Sounds/FX/CEREMA_SIRENNES_POMPIER.mp3)"></a-sound>
<a-sound class="randomfx" autoplay="false" loop="false" src="src: url(Sounds/FX/CEREMA_SIRENNE_COMMUNALE.mp3)"></a-sound>
<!-- a faire crescendo <a-sound autoplay="false" loop="true" src="src: url(CEREMA_BOUCLE_EAU.mp3)"></a-sound> -->
<!-- consider time as HUD text display-->
<a-entity cursor="fuse: true; fuseTimeout: 500"
position="0 0 -1"
geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
material="color: black; shader: flat">
</a-entity>
</a-entity>
<a-box resetanimations position="1 1.4 1" rotation="-180 0 -180" scale=".2 .2 .2"><a-text value="reset" color="black" position="-.2 0 .5"></a-text></a-box>
</a-entity>
<!-- Default lighting injected by A-Frame. --> <a-plane id="ground" position="0 -.2 0" rotation="-90 0 0" width="1000" height="1000" color="lightgrey"></a-plane>
<a-entity light="type: ambient; color: #BBB"></a-entity> <a-entity light="type: ambient; color: #BBB"></a-entity>
<a-entity animation="property: position; to: 5 1 1; dur: 20000; easing: linear; loop: true" <a-entity id="sun" light="type: directional; color: #FFF; castShadow:true;" position="-5 5 0"
light="type: directional; color: #FFF; intensity: 0.6" position="-5 1 1"></a-entity> animation="property: light.intensity; from:0.1; to: 0.6; dur: 20000; easing: linear; loop: true; dir:alternate;"
<!-- to animate but also consider the background color --> animation__pos="property: position; from:-50 50 100; to: 50 50 100; dur: 20000; easing: linear; loop: true">
<a-sky color="#ECECEC"></a-sky> </a-entity>
<a-sound src="src: url(birds.mp3)" autoplay="true" position="0 2 5"></a-sound> <a-entity position="-70 0 -42" osm id="osm"></a-entity>
<a-sound src="src: url(river.mp3)" autoplay="true" position="0 2 5"></a-sound> <!--
<a-entity elevation id="elevation" visible="false"></a-entity>
<a-plane animation="property: position; to: 0 0.4 0; dur: 20000; easing: linear; loop: true" Visuals/TEXTURE_BAT/TEXTURE_BAT_Base_Color.jpg
position="0 -0.5 0" rotation="-90 0 0" width="100" height="100" color="lightblue"></a-plane> Visuals/TEXTURE_BAT/TEXTURE_TOIT_Normal_OpenGL.jpg
Visuals/TEXTURE_BAT/TEXTURE_BAT02_Base_Color.jpg
Visuals/TEXTURE_BAT/TEXTURE_SOL_Base_Color.jpg
Visuals/TEXTURE_BAT/TEXTURE_TOIT_Base_Color.jpg
Visuals/TEXTURE_BAT/TEXTURE_BAT02_Normal_OpenGL.jpg
Visuals/TEXTURE_BAT/TEXTURE_SOL_Normal_OpenGL.jpg
Visuals/TEXTURE_BAT/TEXTURE_BAT_Normal_OpenGL.jpg
Visuals/TEXTURE_EAU/Texture_eau_Base_Color.png
Visuals/TEXTURE_EAU/Texture_eau_Metallic.png
Visuals/TEXTURE_EAU/Texture_eau_Normal_OpenGL.png
Visuals/TEXTURE_EAU/Texture_eau_Roughness.png
Visuals/TEXTURE_EAU/Texture_eau_Height.png
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Ruelle_route.glb
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Ruelle_Boite01.glb
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Ruelle_Trottoir.glb
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Ruelle_Jardin.glb
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Ruelle_Boite02.glb
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Ruelle_Lampadaire.glb
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Baquets_Haie.glb
Visuals/EXPORTS/EXPORTS_gltf/ModelsSolo/AA_Baquets_Barriere.glb
Visuals/EXPORTS/EXPORTS_gltf/AA_Citedesbaquets.glb
Visuals/EXPORTS/EXPORTS_gltf/AA_Ruelle_Arbre.glb
Visuals/EXPORTS/EXPORTS_gltf/AA_Ruelle_Buisson.glb
Visuals/EXPORTS/EXPORTS_gltf/AA_Quaidelaruelle.glb
-->
<!-- offset on test data for easier vr testing -->
<a-gltf-model class="models_ruelle" visible="" position="-140.71021 0.73 -28.54369" scale="1.2 1.2 1.2" rotation="0 -111.8545396388247 0" gltf-model="Visuals/EXPORTS/EXPORTS_gltf/AA_Quaidelaruelle.glb" listosmways=""></a-gltf-model>
<a-gltf-model class="models_baquets" visible="" position="95.71286 -1.33294 -307.27021" scale="1.2 1.2 1.2" rotation="0 179.42402537639782 0" gltf-model="Visuals/EXPORTS/EXPORTS_gltf/AA_Citedesbaquets.glb" listosmways=""></a-gltf-model>
<a-plane id="landscape" position="0 -20 0" scale="10 10 10" rotation="-90 0 -90" width="100" height="100" geometry="segmentsheight: 128; segmentswidth: 128"
material="src: https://wxs.ign.fr/ortho/geoportail/r/wms?LAYERS=ORTHOIMAGERY.ORTHOPHOTOS.BDORTHO&EXCEPTIONS=text/xml&FORMAT=image/jpeg&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&STYLES=&CRS=CRS:84&BBOX=0.470286,49.338484,0.556117,49.381417&WIDTH=4096&HEIGHT=4096; displacementscale: 20; displacementmap: https://wxs.ign.fr/altimetrie/geoportail/r/wms?LAYERS=ELEVATION.ELEVATIONGRIDCOVERAGE.HIGHRES&EXCEPTIONS=text/xml&FORMAT=image/jpeg&SERVICE=WMS&VERSION=1.3.0&REQUEST=GETMAP&STYLES=&CRS=CRS:84&BBOX=0.470286,49.338484,0.556117,49.381417&WIDTH=1024&HEIGHT=1024;" ></a-plane>
<!-- somehow the displacement map does not seem to work anymore despite being accessible. Might have to rollback from git history. -->
<a-plane id="watersim" cerema="" position="0 -50.25 0" rotation="-90 90 0" src="b1rwater0002.jpg"
material="displacementScale: 0.01; wireframe: false; displacementMap: ruelle_sim_data/crop_outpy_1.jpg"
geometry="segmentsHeight: 128; segmentsWidth: 128" scale="1000 1000 100"></a-plane>
<a-sky src="cloudsky.jpg"
animation="from: #000; to: #aaf; loop: true; dur: 20000; dir: alternate; loop:true; property: components.material.material.color; type: color;"
animation_texture="property: material.offset; to: 1 1; dur: 200; easing: linear; loop: true; dir: alternate;"
></a-sky>
</a-scene> </a-scene>
</body> </body>
</html> </html>

Loading…
Cancel
Save