@ -4,84 +4,17 @@
< / 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', {
var simsrc = "simulations/initial_simulation_data/crop_outpy_"
init: function () {
var forceSimulationSource = AFRAME.utils.getUrlParameter('simsrc');
var FXsounds = Array.from( document.querySelectorAll(".randomfx") )
if ( forceSimulationSource ) {
setInterval( _ => { FXsounds[Math.floor(Math.random()*FXsounds.length)].components.sound.playSound() }, 9000 )
simsrc = forceSimulationSource
console.log(simsrc,"switched from default simulation to this one")
// sould also update the src of id="debug_sim_cerema" with a specific step, e.g 50
}
}
})
// 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 simext = ".jpg"
const simstart = 1
const simstart = 1
const simend = 95
const simend = 95 // mostly correct as new simulations got to 96 steps
const sim_filename_padding = true
const sim_filename_padding = true
const animationSpeedStep = 500
const animationSpeedStep = 500
var animationCurrentStep = simstart
var animationCurrentStep = simstart
@ -94,6 +27,10 @@ function animationStep(){
displacementMap: simsrc+(animationCurrentStep++)+simext,
displacementMap: simsrc+(animationCurrentStep++)+simext,
})
})
//wsim.setAttribute("material", "displacementMap", simsrc+(animationCurrentStep++)+simext)
//wsim.setAttribute("material", "displacementMap", simsrc+(animationCurrentStep++)+simext)
//wsim.object3D.children[0].material.needsUpdate = true //no need to do this for every step...
//wsim.object3D.children[0].material.map.offset.setX(animationCurrentStep/1000) // same problem, that offset is not just for the texture but also for the displacement map
// should find a way to decouple
// otherwise could try a custom shader but might still encounter the same problem
if (animationCurrentStep >= simend) resetAnimations()
if (animationCurrentStep >= simend) resetAnimations()
}
}
@ -147,6 +84,13 @@ AFRAME.registerComponent('texture-drift', {
}
}
})
})
AFRAME.registerComponent('randomfx', {
init: function () {
var FXsounds = Array.from( document.querySelectorAll(".randomfx") )
setInterval( _ => { FXsounds[Math.floor(Math.random()*FXsounds.length)].components.sound.playSound() }, 9000 )
}
})
AFRAME.registerComponent('set-initial-camera-from-data', {
AFRAME.registerComponent('set-initial-camera-from-data', {
init: function () {
init: function () {
this.el.setAttribute("position", positionsCurated[selection].worldposition[0] + " 1.6 " + positionsCurated[selection].worldposition[1] )
this.el.setAttribute("position", positionsCurated[selection].worldposition[0] + " 1.6 " + positionsCurated[selection].worldposition[1] )
@ -276,13 +220,15 @@ function fromOSMTo3DElements( josm, el ){
var highways = josm.elements.filter( e => (e.type == "way" & & e.tags & & 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
// not so good, shows paths rather than a usable geometry
if (!disableWaysForGLTFExportInHubs)
if (!disableWaysForGLTFExportInHubs)
highways.map( e => { makeWay( e, wayHeight, el ) })
highways.map( e => { makeWay( e, wayHeight, el, 0x120a06 ) })
}
}
function geolocalized( pos ){
function geolocalized( pos ){
console.log( pos.coords )
console.log( pos.coords )
}
}
const loader = new THREE.TextureLoader();
function addWall(a, b, h, el, color=0x6d6a64){
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
@ -299,7 +245,12 @@ function addWall(a, b, h, el, color=0x6d6a64){
// 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: color, side: THREE.DoubleSide } );
const material = new THREE.MeshBasicMaterial({
color : new THREE.Color( 0x6d6a64 ).offsetHSL(0,0,-Math.random()/10),
side: THREE.DoubleSide,
map: loader.load('Visuals/TEXTURE_BAT/TEXTURE_BAT_Base_Color.jpg'),
});
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
// not sure it's the best level of abstraction, maybe building instead
@ -318,7 +269,10 @@ function addFan(a, b, c, el, verticalOffset, color ){
// 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: color, side: THREE.DoubleSide } );
const 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 );
@ -362,14 +316,14 @@ function getCentroid(points){
return centroid;
return centroid;
}
}
function makeWay( points, height, el ){
function makeWay( points, height, el, color=0xCCCCCC ){
var poly = points.map( p => pointFromCoordinates( p ) )
var poly = points.map( p => pointFromCoordinates( p ) )
var way = document.createElement("a-entity")
var way = document.createElement("a-entity")
//way.setAttribute("position", "0 1 0") // vertical offset to avoid z-fighting on large horizontal planes
//way.setAttribute("position", "0 1 0") // vertical offset to avoid z-fighting on large horizontal planes
way.className = "way"
way.className = "way"
way.setAttribute("shadow", "") // somehow doesn't seem to receive, only casts
way.setAttribute("shadow", "") // somehow doesn't seem to receive, only casts
el.appendChild( way )
el.appendChild( way )
make3DFloor( poly, way )
make3DFloor( poly, way, color, "Visuals/TEXTURE_BAT/TEXTURE_SOL_Base_Color.jpg" )
}
}
function makeLamp( treedata, el){
function makeLamp( treedata, el){
@ -405,7 +359,7 @@ function makeBuilding( points, height, el ){
el.appendChild( building )
el.appendChild( building )
makeWalls( poly, height, building )
makeWalls( poly, height, building )
// make3DFloor( poly, building ) // not visibile so might be removed
// make3DFloor( poly, building ) // not visibile so might be removed
var color = new THREE.Color( 0x8080 FF ).offsetHSL(0,0,-Math.random());
var color = new THREE.Color( 0x8F8F FF ).offsetHSL(0,0,-Math.random());
make3DRoof( poly, building, height, color )
make3DRoof( poly, building, height, color )
building.setAttribute("shadow", "")// somehow doesn't seem to receive it, only casts
building.setAttribute("shadow", "")// somehow doesn't seem to receive it, only casts
@ -489,9 +443,13 @@ AFRAME.registerComponent('hotspots', {
init: function () {
init: function () {
var el = this.el
var el = this.el
hotspots.map( h => {
hotspots.map( h => {
var pointEl = document.createElement("a-sphere")
//var pointEl = document.createElement("a-sphere")
pointEl.setAttribute("opacity", .3)
//pointEl.setAttribute("geometry", "segmentsHeight: 4; segmentsWidth: 8")
pointEl.setAttribute("geometry", "segmentsHeight: 4; segmentsWidth: 8")
var pointEl = document.createElement("a-gltf-model")
pointEl.setAttribute("gltf-model", "Visuals/ASSETS_3D/AA_Hotspot.glb")
//pointEl.setAttribute("opacity", .3)
pointEl.setAttribute("model-opacity", .3)
pointEl.setAttribute("scale", "50 50 50")
pointEl.setAttribute("position", h[0] + " 3 " + h[1])
pointEl.setAttribute("position", h[0] + " 3 " + h[1])
pointEl.setAttribute("teleport-via-cursor", true)
pointEl.setAttribute("teleport-via-cursor", true)
el.appendChild( pointEl )
el.appendChild( pointEl )
@ -500,6 +458,26 @@ AFRAME.registerComponent('hotspots', {
}
}
})
})
// from https://stackoverflow.com/questions/43914818/alpha-animation-in-aframe-for-a-object-model
AFRAME.registerComponent('model-opacity', {
schema: {default: 1.0},
init: function () {
this.el.addEventListener('model-loaded', this.update.bind(this));
},
update: function () {
var mesh = this.el.getObject3D('mesh');
var data = this.data;
if (!mesh) { return; }
mesh.traverse(function (node) {
if (node.isMesh) {
node.material.opacity = data;
node.material.transparent = data < 1.0 ;
node.material.needsUpdate = true;
}
});
}
});
AFRAME.registerComponent('teleport-via-cursor', {
AFRAME.registerComponent('teleport-via-cursor', {
init: function () {
init: function () {
var el = this.el
var el = this.el
@ -510,10 +488,12 @@ AFRAME.registerComponent('teleport-via-cursor', {
// might want to blink too, e.g black sphere around the camera for .5s
// might want to blink too, e.g black sphere around the camera for .5s
});
});
this.el.addEventListener('fusing', function (evt) {
this.el.addEventListener('fusing', function (evt) {
el.setAttribute("opacity", 1)
//el.setAttribute("opacity", 1)
el.setAttribute("model-opacity", 1)
});
});
this.el.addEventListener('mouseleave', function (evt) {
this.el.addEventListener('mouseleave', function (evt) {
el.setAttribute("opacity", 0.3)
//el.setAttribute("opacity", 0.3)
el.setAttribute("model-opacity", .3)
});
});
}
}
});
});
@ -583,14 +563,32 @@ AFRAME.registerComponent('displaymodels', {
})
})
}
}
})
})
AFRAME.registerComponent('forcestats', {
AFRAME.registerComponent('forcestats', {
init: function () {
init: function () {
var forcestats = AFRAME.utils.getUrlParameter('forcestats');
var forcestats = AFRAME.utils.getUrlParameter('forcestats');
console.log(forcestats)
if (forcestats == "true") this.el.setAttribute("stats", true)
if (forcestats == "true") this.el.setAttribute("stats", true)
}
}
})
})
AFRAME.registerComponent('customdebug', {
init: function () {
var el = this.el
var debugmode = AFRAME.utils.getUrlParameter('debugmode');
if (debugmode & & debugmode == "true") {
Array.from( document.querySelectorAll(".debug") ).map( e => e.setAttribute("visible", true) )
document.querySelector("#mainbutton").style.display = "none";
el.addEventListener('loaded', function () {
el.components.inspector.openInspector()
setTimeout(function () { // setTimeout for good measure.
AFRAME.scenes[0].camera.position.set(400,600,-30)
AFRAME.scenes[0].camera.rotation.set(-1.6,0.65,1.6);
}, 1000)
});
}
}
})
function resetAnimations(){
function resetAnimations(){
Array.from( document.querySelectorAll("[animation]") )
Array.from( document.querySelectorAll("[animation]") )
.map( a => Object.keys( a.components )
.map( a => Object.keys( a.components )
@ -618,8 +616,8 @@ function playSound(param){
resetAnimations()
resetAnimations()
}
}
< / script >
< / script >
< button style = "z-index: 1; position: absolute; width:50%; margin: auto; text-align:center; top:45%; left:30%; height:30%;" onclick = "playSound(this)" > Demarrer l'experience< / button >
< button id = mainbutton style = "z-index: 1; position: absolute; width:50%; margin: auto; text-align:center; top:45%; left:30%; height:30%;" onclick = "playSound(this)" > Demarrer l'experience< / button >
< a-scene displaymodels forcestats >
< a-scene displaymodels forcestats customdebug >
< a-entity id = "hotspots" hotspots > < / a-entity >
< 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 >
@ -638,12 +636,13 @@ function playSound(param){
>< / a-entity >
>< / a-entity >
<!-- consider time as HUD text display -->
<!-- consider time as HUD text display -->
< a-entity cursor = "fuse: true; fuseTimeout: 500"
< a-entity cursor = "fuse: true; fuseTimeout: 500"
position="0 0 -1 "
position="0 0 -3 "
geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03 "
geometry="primitive: ring; radiusInner: 0.04; radiusOuter: 0.05 "
material="color: black ; shader: flat">
material="color: orange ; shader: flat">
< / a-entity >
< / a-entity >
< / 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-gltf-model resetanimations = "" position = "6 6 9" scale = "100 100 100" rotation = "29 -160 -10" gltf-model = "Visuals/ASSETS_3D/AA_Reset.glb" opacity = "1" > < / a-gltf-model >
< a-gltf-model position = "-2.2864 1.02726 -1.41235" scale = "30 30 30" rotation = "0 70 0" gltf-model = "Visuals/ASSETS_3D/AA_Crue.glb" opacity = "1" > < / a-gltf-model >
< / a-entity >
< / a-entity >
< a-plane id = "ground" position = "0 -.2 0" rotation = "-90 0 0" width = "1000" height = "1000" color = "lightgrey" > < / a-plane >
< a-plane id = "ground" position = "0 -.2 0" rotation = "-90 0 0" width = "1000" height = "1000" color = "lightgrey" > < / a-plane >
@ -654,11 +653,6 @@ function playSound(param){
< / a-entity >
< / a-entity >
< a-entity position = "-70 0 -42" osm id = "osm" > < / a-entity >
< a-entity position = "-70 0 -42" osm id = "osm" > < / a-entity >
<!--
live layers (deactivated by default)
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 >
-->
<!-- offset on test data for easier vr testing -->
<!-- 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_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 >
@ -666,14 +660,23 @@ material="src: https://wxs.ign.fr/ortho/geoportail/r/wms?LAYERS=ORTHOIMAGERY.ORT
< 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"
< 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: ign_terrain_cached.jpg; displacementScale: 20; displacementMap: ign_elevation_cached.jpg;">< / a-plane >
material="src: ign_terrain_cached.jpg; displacementScale: 20; displacementMap: ign_elevation_cached.jpg;">< / a-plane >
< a-plane id = "watersim" cerema = "" position = "0 -50.25 0" rotation = "-90 90 0" src = "Visuals/TEXTURE_EAU/Texture_eau_Base_Color.png"
< a-plane id = "watersim" cerema = "" position = "0 -50.25 0" rotation = "-90 90 0" src = "Visuals/EAU/Eau_01.jpg"
material="displacementScale: 0.01; wireframe: false; displacementMap: ruelle_sim_data/crop_outpy_1.jpg "
material="displacementScale: 0.02; wireframe: false; displacementMap: "
geometry="segmentsHeight: 128; segmentsWidth: 128" scale="1000 1000 100">< / a-plane >
geometry="segmentsHeight: 128; segmentsWidth: 128" scale="1000 1000 100">< / a-plane >
<!-- animation might fail due texture.neddUpdate unset -->
< a-image id = "debug_baquet_osm" src = "baquet_osm.png" visible = false position = "-235 1.1 145" rotation = "-90 90 0" scale = "200 200 200" material = "opacity: 0.5" class = debug > < / a-image >
< a-image id = "debug_ruelle_osm" src = "ruelle_osm.png" visible = false position = "-40 1 -220" rotation = "-90 90 0" material = "opacity: 0.5" scale = "200 200 200" class = "debug" > < / a-image >
< a-image id = "debug_sim_cerema" src = "" visible = false position = "0 1.2 0" rotation = "-90 90 0" material = "opacity: 0.5" scale = "1000 1000 1000" class = "debug" > < / a-image >
< a-sky src = "cloudsky.jpg"
< 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="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;"
animation_texture="property: material.offset; to: 1 1; dur: 200; easing: linear; loop: true; dir: alternate;"
>< / a-sky >
>< / a-sky >
< / a-scene >
< / a-scene >
<!--
live layers (deactivated by default)
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 >
-->
< / body >
< / body >
< / html >
< / html >