Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Alfakhori
Gengenbach
Commits
499216b7
Commit
499216b7
authored
Oct 05, 2026
by
Alfakhori
Browse files
three v186
parent
ebd6d6fc
Pipeline
#12484
passed with stage
in 26 seconds
Changes
5
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
public/ar_download.js
View file @
499216b7
function
downloadScene
()
{
const
placedModels
=
getAllPlacedModels
().
map
(
model
=>
{
const
{
lat
,
lng
}
=
threeToLeaflet
(
model
.
position
.
x
,
model
.
position
.
z
);
return
{
name
:
model
.
modelConfig
.
name
,
position
:
model
.
position
,
rotation
:
model
.
rotation
,
scale
:
model
.
scale
,
lat
:
lat
,
lng
:
lng
,
}
});
const
placedModels
=
window
.
getAllPlacedModels
().
map
((
model
)
=>
{
const
{
lat
,
lng
}
=
window
.
threeToLeaflet
(
model
.
position
.
x
,
model
.
position
.
z
,
);
return
{
name
:
model
.
modelConfig
.
name
,
position
:
model
.
position
,
rotation
:
model
.
rotation
,
scale
:
model
.
scale
,
lat
:
lat
,
lng
:
lng
,
};
});
console
.
log
(
"
Modelle:
"
+
placedModels
.
length
)
if
(
placedModels
.
length
===
0
)
{
showInfoDialog
(
"
Szene ist leer
"
);
return
;
}
console
.
log
(
"
Modelle:
"
+
placedModels
.
length
)
;
if
(
placedModels
.
length
===
0
)
{
showInfoDialog
(
"
Szene ist leer
"
);
return
;
}
if
(
!
geoLocation
)
{
console
.
log
(
"
Standort nicht geladen
"
);
showInfoDialog
(
"
Ihr Standort wurde noch nicht geladen
"
);
return
;
}
if
(
!
geoLocation
)
{
console
.
log
(
"
Standort nicht geladen
"
);
showInfoDialog
(
"
Ihr Standort wurde noch nicht geladen
"
);
return
;
}
const
data
=
{
lat
:
geoLocation
.
latitude
,
lng
:
geoLocation
.
longitude
,
models
:
placedModels
,
}
const
data
=
{
lat
:
geoLocation
.
latitude
,
lng
:
geoLocation
.
longitude
,
models
:
placedModels
,
};
const
sceneData
=
JSON
.
stringify
(
data
,
null
,
1
);
const
blob
=
new
Blob
([
sceneData
],
{
type
:
"
application/json
"
});
const
url
=
URL
.
createObjectURL
(
blob
);
const
a
=
document
.
createElement
(
"
a
"
);
a
.
href
=
url
;
a
.
download
=
"
scene.json
"
;
a
.
style
.
display
=
"
none
"
;
const
sceneData
=
JSON
.
stringify
(
data
,
null
,
1
);
const
blob
=
new
Blob
([
sceneData
],
{
type
:
"
application/json
"
});
const
url
=
URL
.
createObjectURL
(
blob
);
const
a
=
document
.
createElement
(
"
a
"
);
a
.
href
=
url
;
a
.
download
=
"
scene.json
"
;
a
.
style
.
display
=
"
none
"
;
document
.
body
.
appendChild
(
a
);
a
.
click
();
URL
.
revokeObjectURL
(
url
);
document
.
body
.
removeChild
(
a
);
document
.
body
.
appendChild
(
a
);
a
.
click
();
URL
.
revokeObjectURL
(
url
);
document
.
body
.
removeChild
(
a
);
}
function
loadSceneFromFile
(
loadedData
)
{
const
fileInput
=
document
.
createElement
(
'
input
'
);
fileInput
.
type
=
'
file
'
;
fileInput
.
accept
=
'
application/json
'
;
const
fileInput
=
document
.
createElement
(
"
input
"
);
fileInput
.
type
=
"
file
"
;
fileInput
.
accept
=
"
application/json
"
;
fileInput
.
addEventListener
(
'
change
'
,
function
(
event
)
{
const
file
=
event
.
target
.
files
[
0
];
const
fileName
=
file
?
file
.
name
:
"
Keine Datei ausgewählt
"
if
(
!
file
)
{
console
.
log
(
"
Keine Datei ausgewählt
"
);
return
;
}
fileInput
.
addEventListener
(
"
change
"
,
function
(
event
)
{
const
file
=
event
.
target
.
files
[
0
];
const
fileName
=
file
?
file
.
name
:
"
Keine Datei ausgewählt
"
;
if
(
!
file
)
{
console
.
log
(
"
Keine Datei ausgewählt
"
);
return
;
}
const
reader
=
new
FileReader
();
reader
.
onload
=
function
(
e
)
{
try
{
const
sceneData
=
JSON
.
parse
(
e
.
target
.
result
);
loadedData
(
sceneData
,
fileName
);
}
catch
(
error
)
{
console
.
error
(
"
Fehler beim Verarbeiten der Datei:
"
,
error
);
}
};
const
reader
=
new
FileReader
();
reader
.
onload
=
function
(
e
)
{
try
{
const
sceneData
=
JSON
.
parse
(
e
.
target
.
result
);
loadedData
(
sceneData
,
fileName
);
}
catch
(
error
)
{
console
.
error
(
"
Fehler beim Verarbeiten der Datei:
"
,
error
);
}
};
reader
.
readAsText
(
file
);
reader
.
readAsText
(
file
);
});
fileInput
.
click
();
}
\ No newline at end of file
}
/* Expose functions used by the existing HTML and ar_start.js. */
window
.
downloadScene
=
downloadScene
;
window
.
loadSceneFromFile
=
loadSceneFromFile
;
public/ar_main.js
View file @
499216b7
import
*
as
THREE
from
"
three
"
;
import
{
GLTFLoader
}
from
"
three/addons/loaders/GLTFLoader.js
"
;
/* ========================= */
/* GLOBALE VARIABLEN */
/* ========================= */
...
...
@@ -6,133 +9,122 @@ let selectedPlacedModel = null;
let
currentSession
=
null
;
let
reticle
=
null
;
let
scene
,
camera
;
let
xrRenderer
=
null
;
let
geoLocation
;
const
modelCache
=
new
Map
();
// PERFORMANCE: Reuse one GLTFLoader instead of creating
// a new loader every time a model is placed.
const
gltfLoader
=
new
THREE
.
GLTFLoader
();
const
gltfLoader
=
new
GLTFLoader
();
const
menus
=
[
'
menu-bar
'
,
'
add-menu
'
,
'
edit-menu
'
,
'
options-menu
'
,
'
map-window
'
"
menu-bar
"
,
"
add-menu
"
,
"
edit-menu
"
,
"
options-menu
"
,
"
map-window
"
,
];
// PERFORMANCE: Cache menu DOM elements once instead of
// repeatedly calling document.getElementById() in showMenu().
const
menuElements
=
{};
menus
.
forEach
(
id
=>
{
menuElements
[
id
]
=
document
.
getElementById
(
id
);
menus
.
forEach
(
(
id
)
=>
{
menuElements
[
id
]
=
document
.
getElementById
(
id
);
});
/* ========================= */
/* MODELLE */
/* ========================= */
let
models
=
{
bench
:
{
name
:
"
Bench
"
,
image
:
"
assets/previewImages/bench.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/bench_model/scene.gltf
"
,
scale
:
{
x
:
0.1
,
y
:
0.1
,
z
:
0.1
},
minScale
:
0.05
,
maxScale
:
0.5
},
trashbin
:
{
name
:
"
Trash bin
"
,
image
:
"
assets/previewImages/trash_can.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/trash_model/scene.gltf
"
,
scale
:
{
x
:
0.03
,
y
:
0.03
,
z
:
0.03
},
minScale
:
0.01
,
maxScale
:
0.1
},
lantern
:
{
name
:
"
Lantern
"
,
image
:
"
assets/previewImages/park_light.png
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/park_light_model/scene.gltf
"
,
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.2
,
maxScale
:
5
},
telephone_box
:
{
name
:
"
Telephone Box
"
,
image
:
"
assets/previewImages/telephone_box.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/telephone_box_model/scene.gltf
"
,
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.05
,
maxScale
:
1
},
fire_hydrant_model
:
{
name
:
"
Fire Hydrant
"
,
image
:
"
assets/previewImages/hydrant.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fire_hydrant_model/scene.gltf
"
,
scale
:
{
x
:
0.3
,
y
:
0.3
,
z
:
0.3
},
minScale
:
0.1
,
maxScale
:
1
},
statue
:
{
name
:
"
Statue
"
,
image
:
"
assets/previewImages/statue.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/statue_model/scene.gltf
"
,
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.05
,
maxScale
:
2
},
fountain
:
{
name
:
"
Fountain
"
,
image
:
"
assets/previewImages/fountain.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fountain_model/scene.gltf
"
,
scale
:
{
x
:
0.001
,
y
:
0.001
,
z
:
0.001
},
minScale
:
0.0005
,
maxScale
:
0.005
}
bench
:
{
name
:
"
Bench
"
,
image
:
"
assets/previewImages/bench.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/bench_model/scene.gltf
"
,
scale
:
{
x
:
0.1
,
y
:
0.1
,
z
:
0.1
},
minScale
:
0.05
,
maxScale
:
0.5
,
},
trashbin
:
{
name
:
"
Trash bin
"
,
image
:
"
assets/previewImages/trash_can.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/trash_model/scene.gltf
"
,
scale
:
{
x
:
0.03
,
y
:
0.03
,
z
:
0.03
},
minScale
:
0.01
,
maxScale
:
0.1
,
},
lantern
:
{
name
:
"
Lantern
"
,
image
:
"
assets/previewImages/park_light.png
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/park_light_model/scene.gltf
"
,
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.2
,
maxScale
:
5
,
},
telephone_box
:
{
name
:
"
Telephone Box
"
,
image
:
"
assets/previewImages/telephone_box.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/telephone_box_model/scene.gltf
"
,
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.05
,
maxScale
:
1
,
},
fire_hydrant_model
:
{
name
:
"
Fire Hydrant
"
,
image
:
"
assets/previewImages/hydrant.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fire_hydrant_model/scene.gltf
"
,
scale
:
{
x
:
0.3
,
y
:
0.3
,
z
:
0.3
},
minScale
:
0.1
,
maxScale
:
1
,
},
statue
:
{
name
:
"
Statue
"
,
image
:
"
assets/previewImages/statue.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/statue_model/scene.gltf
"
,
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.05
,
maxScale
:
2
,
},
fountain
:
{
name
:
"
Fountain
"
,
image
:
"
assets/previewImages/fountain.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fountain_model/scene.gltf
"
,
scale
:
{
x
:
0.001
,
y
:
0.001
,
z
:
0.001
},
minScale
:
0.0005
,
maxScale
:
0.005
,
},
};
/* ========================= */
/* INITIALISIERUNG */
/* ========================= */
window
.
onload
=
()
=>
{
initializeAddMenu
();
initializeAddMenu
();
// Allen Buttons den Sound hinzufügen
const
buttons
=
document
.
querySelectorAll
(
"
button, .menu-item
"
);
// Allen Buttons den Sound hinzufügen
const
buttons
=
document
.
querySelectorAll
(
"
button, .menu-item
"
);
buttons
.
forEach
(
button
=>
{
button
.
addEventListener
(
"
click
"
,
playButtonSound
);
});
buttons
.
forEach
((
button
)
=>
{
button
.
addEventListener
(
"
click
"
,
playButtonSound
);
});
};
function
initializeAddMenu
()
{
const
addMenu
=
document
.
getElementById
(
"
add-menu
"
);
const
addMenu
=
document
.
getElementById
(
'
add-menu
'
);
addMenu
.
innerHTML
=
Object
.
entries
(
models
)
.
map
(
([
key
,
model
])
=>
`
addMenu
.
innerHTML
=
Object
.
entries
(
models
)
.
map
(
([
key
,
model
])
=>
`
<div
class="menu-item"
id="
${
key
}
-item"
...
...
@@ -143,11 +135,10 @@ function initializeAddMenu() {
alt="
${
model
.
name
}
"
/>
</div>
`
)
.
join
(
''
)
+
`
`
,
)
.
join
(
""
)
+
`
<div
class="menu-item"
onclick="showMenu('menu-bar')"
...
...
@@ -160,617 +151,351 @@ function initializeAddMenu() {
`
;
}
/* ========================= */
/* MENÜ-STEUERUNG */
/* ========================= */
function
showMenu
(
menuId
)
{
const
isMapWindow
=
menuId
===
"
map-window
"
;
const
isMapWindow
=
menuId
===
'
map-window
'
;
// Check location before opening map
if
(
isMapWindow
&&
!
geoLocation
)
{
console
.
log
(
"
Standort nicht geladen
"
);
// Check location before opening map
if
(
isMapWindow
&&
!
geoLocation
)
{
showInfoDialog
(
"
Ihr Standort wurde noch nicht geladen
"
);
console
.
log
(
"
Standort nicht geladen
"
);
return
;
}
showInfoDialog
(
"
Ihr Standort wurde noch nicht geladen
"
);
// PERFORMANCE: Use cached menu elements instead of
// repeatedly querying the DOM.
menus
.
forEach
((
id
)
=>
{
const
element
=
menuElements
[
id
];
return
;
if
(
element
)
{
element
.
style
.
display
=
id
===
menuId
?
"
flex
"
:
"
none
"
;
}
});
closeDynamicMenu
();
// PERFORMANCE: Use cached menu elements instead of
// repeatedly querying the DOM.
menus
.
forEach
(
id
=>
{
const
element
=
menuElements
[
id
];
if
(
element
)
{
element
.
style
.
display
=
id
===
menuId
?
'
flex
'
:
'
none
'
;
}
});
closeDynamicMenu
();
// Menu-specific actions
if
(
menuId
===
'
menu-bar
'
)
{
clearSelectedModel
();
}
else
if
(
isMapWindow
)
{
// Initialize map
init_map
();
// Leaflet needs to know the actual size of the
// visible map container.
setTimeout
(()
=>
{
if
(
typeof
centerOnPlan
===
'
function
'
)
{
centerOnPlan
();
}
// Menu-specific actions
if
(
menuId
===
"
menu-bar
"
)
{
clearSelectedModel
();
}
else
if
(
isMapWindow
)
{
// Initialize map
init_map
();
},
100
);
}
// Leaflet needs to know the actual size of the
// visible map container.
setTimeout
(()
=>
{
if
(
typeof
centerOnPlan
===
"
function
"
)
{
centerOnPlan
();
}
},
100
);
}
}
/* ============================================================ */
/* Close map and return to AR scene */
/* ============================================================ */
function
closeMap
()
{
const
mapWindow
=
menuElements
[
"
map-window
"
];
const
mapWindow
=
menuElements
[
'
map-window
'
];
if
(
mapWindow
)
{
mapWindow
.
style
.
display
=
"
none
"
;
}
// Show the main AR menu again
const
menuBar
=
menuElements
[
'
menu-bar
'
];
if
(
menuBar
)
{
menuBar
.
style
.
display
=
"
flex
"
;
}
if
(
mapWindow
)
{
mapWindow
.
style
.
display
=
"
none
"
;
}
// Stop the map update interval
if
(
typeof
intervalId
!==
'
undefined
'
)
{
// Show the main AR menu again
const
menuBar
=
menuElements
[
"
menu-bar
"
];
if
(
intervalId
)
{
if
(
menuBar
)
{
menuBar
.
style
.
display
=
"
flex
"
;
}
clearInterval
(
intervalId
);
// Stop the map update interval
if
(
typeof
intervalId
!==
"
undefined
"
)
{
if
(
intervalId
)
{
clearInterval
(
intervalId
);
intervalId
=
null
;
}
intervalId
=
null
;
}
}
// Clear selected model
clearSelectedModel
();
// Clear selected model
clearSelectedModel
();
}
function
closeDynamicMenu
()
{
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
if
(
dynamicMenu
)
{
dynamicMenu
.
style
.
display
=
"
none
"
;
}
if
(
dynamicMenu
)
{
dynamicMenu
.
style
.
display
=
"
none
"
;
}
}
function
showInfoDialog
(
message
)
{
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
const
infoText
=
document
.
getElementById
(
"
info-text
"
);
const
infoText
=
document
.
getElementById
(
"
info-text
"
);
infoText
.
textContent
=
message
;
infoText
.
textContent
=
message
;
infoDialog
.
style
.
display
=
"
flex
"
;
infoDialog
.
style
.
display
=
"
flex
"
;
}
function
closeInfoDialog
()
{
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
infoDialog
.
style
.
display
=
"
none
"
;
infoDialog
.
style
.
display
=
"
none
"
;
}
/* ========================= */
/* MODELL-HANDLING */
/* ========================= */
function
getAllPlacedModels
()
{
return
scene
.
children
.
filter
(
child
=>
child
.
isPlacedModel
===
true
);
return
scene
.
children
.
filter
((
child
)
=>
child
.
isPlacedModel
===
true
);
}
function
loadModel
(
filePath
)
{
placeModel
(
filePath
,
reticle
.
position
,
(
model
)
=>
{
selectedPlacedModel
=
model
;
placeModel
(
filePath
,
reticle
.
position
,
(
model
)
=>
{
selectedPlacedModel
=
model
;
highlightSelectedModel
();
highlightSelectedModel
();
showMenu
(
'
edit-menu
'
);
}
);
showMenu
(
"
edit-menu
"
);
});
}
function
placeModel
(
filePath
,
position
,
onPlace
=
null
)
{
const
modelConfig
=
Object
.
values
(
models
).
find
(
(
model
)
=>
model
.
file
===
filePath
,
);
function
placeModel
(
filePath
,
position
,
onPlace
=
null
)
{
const
modelConfig
=
Object
.
values
(
models
)
.
find
(
model
=>
model
.
file
===
filePath
);
// PERFORMANCE: Reuse the global GLTFLoader.
// Do not create a new loader for every model.
if
(
modelCache
.
has
(
filePath
))
{
// Use the already loaded model
const
cachedModel
=
modelCache
.
get
(
filePath
);
const
model
=
cachedModel
.
clone
(
true
);
// PERFORMANCE: Reuse the global GLTFLoader.
// Do not create a new loader for every model.
if
(
modelCache
.
has
(
filePath
))
{
if
(
modelConfig
&&
modelConfig
.
scale
)
{
model
.
scale
.
set
(
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
z
,
);
}
// Use the already loaded model
const
cachedModel
=
modelCache
.
get
(
filePath
);
model
.
position
.
copy
(
position
);
const
model
=
cachedModel
.
clone
(
true
);
model
.
isPlacedModel
=
true
;
model
.
modelConfig
=
modelConfig
;
if
(
modelConfig
&&
modelConfig
.
scale
)
{
scene
.
add
(
model
);
model
.
scale
.
set
(
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
z
);
if
(
onPlace
)
{
onPlace
(
model
);
}
}
else
{
// Model has not been loaded yet
gltfLoader
.
load
(
filePath
,
(
gltf
)
=>
{
// Store the original model in the cache
modelCache
.
set
(
filePath
,
gltf
.
scene
);
const
model
=
gltf
.
scene
.
clone
(
true
);
if
(
modelConfig
&&
modelConfig
.
scale
)
{
model
.
scale
.
set
(
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
z
,
);
}
model
.
position
.
copy
(
position
);
model
.
position
.
copy
(
position
);
model
.
isPlacedModel
=
true
;
model
.
isPlacedModel
=
true
;
model
.
modelConfig
=
modelConfig
;
model
.
modelConfig
=
modelConfig
;
scene
.
add
(
model
);
if
(
onPlace
)
{
onPlace
(
model
);
onPlace
(
model
);
}
},
}
else
{
// Model has not been loaded yet
gltfLoader
.
load
(
filePath
,
(
gltf
)
=>
{
// Store the original model in the cache
modelCache
.
set
(
filePath
,
gltf
.
scene
);
const
model
=
gltf
.
scene
.
clone
(
true
);
if
(
modelConfig
&&
modelConfig
.
scale
)
{
model
.
scale
.
set
(
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
z
);
}
model
.
position
.
copy
(
position
);
model
.
isPlacedModel
=
true
;
model
.
modelConfig
=
modelConfig
;
scene
.
add
(
model
);
if
(
onPlace
)
{
onPlace
(
model
);
}
},
undefined
,
(
error
)
=>
{
undefined
,
console
.
error
(
"
Fehler beim Laden des Modells:
"
,
error
);
}
);
}
(
error
)
=>
{
console
.
error
(
"
Fehler beim Laden des Modells:
"
,
error
);
},
);
}
}
function
selectModel
(
modelId
)
{
const
model
=
models
[
modelId
];
const
model
=
models
[
modelId
];
if
(
model
&&
model
.
file
)
{
if
(
model
&&
model
.
file
)
{
loadModel
(
model
.
file
);
loadModel
(
model
.
file
);
showMenu
(
'
menu-bar
'
);
}
showMenu
(
"
menu-bar
"
);
}
}
function
selectModelFromScene
(
event
)
{
const
mouse
=
new
THREE
.
Vector2
(
(
event
.
clientX
/
window
.
innerWidth
)
*
2
-
1
,
const
mouse
=
new
THREE
.
Vector2
(
(
event
.
clientX
/
window
.
innerWidth
)
*
2
-
1
,
-
(
event
.
clientY
/
window
.
innerHeight
)
*
2
+
1
);
const
raycaster
=
new
THREE
.
Raycaster
();
raycaster
.
setFromCamera
(
mouse
,
camera
);
const
intersects
=
raycaster
.
intersectObjects
(
scene
.
children
,
true
);
if
(
intersects
.
length
>
0
)
{
let
selectedObject
=
intersects
[
0
].
object
;
-
(
event
.
clientY
/
window
.
innerHeight
)
*
2
+
1
,
);
const
raycaster
=
new
THREE
.
Raycaster
();
while
(
selectedObject
.
parent
&&
selectedObject
.
parent
!==
scene
)
{
const
rayCamera
=
xrRenderer
&&
xrRenderer
.
xr
.
isPresenting
?
xrRenderer
.
xr
.
getCamera
(
camera
)
:
camera
;
selectedObject
=
selectedObject
.
parent
;
}
raycaster
.
setFromCamera
(
mouse
,
rayCamera
);
const
intersects
=
raycaster
.
intersectObjects
(
scene
.
children
,
true
);
// Reticle cannot be selected
if
(
selectedObject
===
reticle
)
{
if
(
intersects
.
length
>
0
)
{
let
selectedObject
=
intersects
[
0
].
object
;
console
.
log
(
"
Reticle kann nicht ausgewählt werden.
"
);
return
;
}
while
(
selectedObject
.
parent
&&
selectedObject
.
parent
!==
scene
)
{
selectedObject
=
selectedObject
.
parent
;
}
// Reticle cannot be selected
if
(
selectedObject
===
reticle
)
{
console
.
log
(
"
Reticle kann nicht ausgewählt werden.
"
);
selectedPlacedModel
=
selectedObject
;
return
;
}
selectedPlacedModel
=
selectedObject
;
highlightSelectedModel
();
highlightSelectedModel
();
showMenu
(
"
edit-menu
"
);
}
showMenu
(
"
edit-menu
"
);
}
}
function
clearSelectedModel
()
{
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
traverse
((
child
)
=>
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
});
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
traverse
(
(
child
)
=>
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
}
);
selectedPlacedModel
=
null
;
}
selectedPlacedModel
=
null
;
}
}
function
highlightSelectedModel
()
{
if
(
selectedPlacedModel
)
{
removeHighlightFromAllModels
();
if
(
selectedPlacedModel
)
{
removeHighlightFromAllModels
();
selectedPlacedModel
.
traverse
(
(
child
)
=>
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0xff0000
);
}
}
);
}
selectedPlacedModel
.
traverse
((
child
)
=>
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0xff0000
);
}
});
}
}
function
removeHighlightFromSelectedModel
()
{
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
traverse
(
(
child
)
=>
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
}
);
}
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
traverse
((
child
)
=>
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
});
}
}
function
removeHighlightFromAllModels
()
{
scene
.
traverse
(
(
child
)
=>
{
if
(
child
.
isMesh
&&
child
.
material
&&
child
.
material
.
emissive
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
}
);
scene
.
traverse
((
child
)
=>
{
if
(
child
.
isMesh
&&
child
.
material
&&
child
.
material
.
emissive
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
});
}
function
deleteModel
()
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.
"
,
);
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.
"
);
return
;
}
const
deleteDialog
=
document
.
getElementById
(
'
delete-confirmation-dialog
'
);
return
;
}
const
deleteDialog
=
document
.
getElementById
(
"
delete-confirmation-dialog
"
);
deleteDialog
.
style
.
display
=
'
flex
'
;
deleteDialog
.
style
.
display
=
"
flex
"
;
}
function
confirmDelete
(
shouldDelete
)
{
const
deleteDialog
=
document
.
getElementById
(
"
delete-confirmation-dialog
"
);
function
confirmDelete
(
shouldDelete
)
{
const
deleteDialog
=
document
.
getElementById
(
'
delete-confirmation-dialog
'
);
deleteDialog
.
style
.
display
=
'
none
'
;
deleteDialog
.
style
.
display
=
"
none
"
;
if
(
shouldDelete
&&
selectedPlacedModel
)
{
if
(
shouldDelete
&&
selectedPlacedModel
)
{
scene
.
remove
(
selectedPlacedModel
);
scene
.
remove
(
selectedPlacedModel
);
selectedPlacedModel
=
null
;
selectedPlacedModel
=
null
;
showMenu
(
'
menu-bar
'
);
}
showMenu
(
"
menu-bar
"
);
}
}
function
completeEditing
()
{
removeHighlightFromSelectedModel
();
removeHighlightFromSelectedModel
();
closeDynamicMenu
();
closeDynamicMenu
()
;
selectedPlacedModel
=
null
;
selectedPlacedModel
=
null
;
document
.
getElementById
(
"
edit-menu
"
).
style
.
display
=
"
none
"
;
document
.
getElementById
(
'
edit-menu
'
).
style
.
display
=
'
none
'
;
document
.
getElementById
(
'
menu-bar
'
).
style
.
display
=
'
flex
'
;
document
.
getElementById
(
"
menu-bar
"
).
style
.
display
=
"
flex
"
;
}
/* ========================= */
/* BEARBEITUNGS-MENÜS */
/* ========================= */
function
openRotationMenu
()
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
,
);
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
);
return
;
}
const
currentRotation
=
Math
.
round
(
THREE
.
MathUtils
.
radToDeg
(
selectedPlacedModel
.
rotation
.
y
)
);
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
return
;
}
const
currentRotation
=
Math
.
round
(
THREE
.
MathUtils
.
radToDeg
(
selectedPlacedModel
.
rotation
.
y
),
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
dynamicMenu
.
innerHTML
=
`
dynamicMenu
.
innerHTML
=
`
<h3>Rotation anpassen</h3>
...
...
@@ -795,85 +520,45 @@ function openRotationMenu() {
`
;
}
function
updateRotation
(
axis
,
value
)
{
if
(
selectedPlacedModel
)
{
const
radians
=
(
value
/
180
)
*
Math
.
PI
;
function
updateRotation
(
axis
,
value
)
{
selectedPlacedModel
.
rotation
[
axis
]
=
radians
;
if
(
selectedPlacedModel
)
{
const
currentRotationDisplay
=
document
.
getElementById
(
"
current-rotation
"
);
const
radians
=
(
value
/
180
)
*
Math
.
PI
;
selectedPlacedModel
.
rotation
[
axis
]
=
radians
;
const
currentRotationDisplay
=
document
.
getElementById
(
"
current-rotation
"
);
if
(
currentRotationDisplay
)
{
currentRotationDisplay
.
textContent
=
value
;
}
if
(
currentRotationDisplay
)
{
currentRotationDisplay
.
textContent
=
value
;
}
}
}
function
openScaleMenu
()
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
,
);
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
);
return
;
}
const
currentScale
=
selectedPlacedModel
.
scale
.
x
;
const
minScale
=
selectedPlacedModel
.
modelConfig
.
minScale
;
const
maxScale
=
selectedPlacedModel
.
modelConfig
.
maxScale
;
const
step
=
(
maxScale
-
minScale
)
/
100
;
return
;
}
const
currentScale
=
selectedPlacedModel
.
scale
.
x
;
const
currentScalePercent
=
(
(
currentScale
-
minScale
)
/
(
maxScale
-
minScale
)
)
*
100
;
const
minScale
=
selectedPlacedModel
.
modelConfig
.
minScale
;
const
maxScale
=
selectedPlacedModel
.
modelConfig
.
maxScale
;
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
const
step
=
(
maxScale
-
minScale
)
/
100
;
const
currentScalePercent
=
((
currentScale
-
minScale
)
/
(
maxScale
-
minScale
))
*
100
;
dynamicMenu
.
style
.
display
=
"
flex
"
;
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
dynamicMenu
.
innerHTML
=
`
dynamicMenu
.
innerHTML
=
`
<h3>Skalierung anpassen</h3>
...
...
@@ -903,69 +588,36 @@ function openScaleMenu() {
`
;
}
function
updateScale
(
percentValue
,
minScale
,
maxScale
)
{
if
(
selectedPlacedModel
)
{
const
scale
=
minScale
+
(
percentValue
/
100
)
*
(
maxScale
-
minScale
);
function
updateScale
(
percentValue
,
minScale
,
maxScale
)
{
if
(
selectedPlacedModel
)
{
const
scale
=
minScale
+
(
percentValue
/
100
)
*
(
maxScale
-
minScale
);
selectedPlacedModel
.
scale
.
set
(
scale
,
scale
,
scale
);
const
scaleValueDisplay
=
document
.
getElementById
(
"
scale-value
"
);
selectedPlacedModel
.
scale
.
set
(
scale
,
scale
,
scale
);
const
scaleValueDisplay
=
document
.
getElementById
(
"
scale-value
"
);
if
(
scaleValueDisplay
)
{
scaleValueDisplay
.
textContent
=
`
${
parseInt
(
percentValue
,
10
)}
%`
;
}
if
(
scaleValueDisplay
)
{
scaleValueDisplay
.
textContent
=
`
${
parseInt
(
percentValue
,
10
)}
%`
;
}
}
}
let
moveDelta
=
0.1
;
function
openMoveMenu
()
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
,
);
if
(
!
selectedPlacedModel
)
{
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
);
return
;
}
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
return
;
}
dynamicMenu
.
style
.
display
=
"
flex
"
;
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
dynamicMenu
.
innerHTML
=
`
dynamicMenu
.
innerHTML
=
`
<h3>Position anpassen</h3>
...
...
@@ -1044,70 +696,38 @@ function openMoveMenu() {
`
;
}
function
updateMoveDelta
(
value
)
{
moveDelta
=
parseFloat
(
value
);
function
updateMoveDelta
(
value
)
{
moveDelta
=
parseFloat
(
value
);
const
moveDeltaDisplay
=
document
.
getElementById
(
"
move-delta-display
"
);
const
moveDeltaDisplay
=
document
.
getElementById
(
"
move-delta-display
"
);
if
(
moveDeltaDisplay
)
{
moveDeltaDisplay
.
textContent
=
moveDelta
.
toFixed
(
2
);
}
if
(
moveDeltaDisplay
)
{
moveDeltaDisplay
.
textContent
=
moveDelta
.
toFixed
(
2
);
}
}
function
moveModelDynamic
(
axis
,
value
)
{
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
position
[
axis
]
+=
value
;
}
function
moveModelDynamic
(
axis
,
value
)
{
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
position
[
axis
]
+=
value
;
}
}
/* ========================= */
/* KARTENSTEUERUNG */
/* ========================= */
function
refreshMapDialog
()
{
const
mapDialog
=
document
.
getElementById
(
"
map-dialog
"
);
const
mapDialog
=
document
.
getElementById
(
'
map-dialog
'
);
mapDialog
.
style
.
display
=
'
flex
'
;
mapDialog
.
style
.
display
=
"
flex
"
;
}
function
closeMapDialog
()
{
const
mapDialog
=
document
.
getElementById
(
"
map-dialog
"
);
const
mapDialog
=
document
.
getElementById
(
'
map-dialog
'
);
mapDialog
.
style
.
display
=
'
none
'
;
mapDialog
.
style
.
display
=
"
none
"
;
}
/* ========================= */
/* AR-HANDLING */
/* ========================= */
...
...
@@ -1117,61 +737,63 @@ async function activateXR(sceneData = null) {
document
.
body
.
appendChild
(
canvas
);
const
gl
=
canvas
.
getContext
(
"
webgl
"
,
{
xrCompatible
:
true
,
});
// ========================================================
// Three.js WebXR renderer
// ========================================================
const
renderer
=
new
THREE
.
WebGLRenderer
({
alpha
:
true
,
antialias
:
true
,
canvas
,
context
:
gl
,
});
renderer
.
autoClear
=
false
;
renderer
.
xr
.
enabled
=
true
;
xrRenderer
=
renderer
;
scene
=
new
THREE
.
Scene
();
// Keep the same reference-space type used by the previous
// manual WebXR implementation.
renderer
.
xr
.
setReferenceSpaceType
(
"
local
"
);
scene
=
new
THREE
.
Scene
();
camera
=
new
THREE
.
PerspectiveCamera
();
camera
.
matrixAutoUpdate
=
false
;
// ========================================================
// AR lighting
// ========================================================
// Soft overall illumination
const
ambientLight
=
new
THREE
.
AmbientLight
(
0xffffff
,
2.5
);
scene
.
add
(
ambientLight
);
// Sky/ground illumination
const
hemisphereLight
=
new
THREE
.
HemisphereLight
(
0xffffff
,
0x888888
,
1.5
);
scene
.
add
(
hemisphereLight
);
// Directional light for shadows and form
const
directionalLight
=
new
THREE
.
DirectionalLight
(
0xffffff
,
1.5
);
directionalLight
.
position
.
set
(
10
,
20
,
10
);
scene
.
add
(
directionalLight
);
// ========================================================
// Reticle
// ========================================================
const
loader
=
new
THREE
.
GLTFLoader
();
const
loader
=
new
GLTFLoader
();
loader
.
load
(
"
https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf
"
,
(
gltf
)
=>
{
reticle
=
gltf
.
scene
;
reticle
.
visible
=
false
;
scene
.
add
(
reticle
);
},
undefined
,
(
error
)
=>
{
console
.
error
(
"
Fehler beim Laden des Reticles:
"
,
error
);
},
);
// ========================================================
...
...
@@ -1188,11 +810,14 @@ async function activateXR(sceneData = null) {
requiredFeatures
:
[
"
hit-test
"
],
});
currentSession
.
updateRenderState
({
baseLayer
:
new
XRWebGLLayer
(
currentSession
,
gl
),
});
// Let Three.js manage the XR render state / framebuffer.
await
renderer
.
xr
.
setSession
(
currentSession
);
const
referenceSpace
=
await
currentSession
.
requestReferenceSpace
(
"
local
"
);
const
referenceSpace
=
renderer
.
xr
.
getReferenceSpace
();
if
(
!
referenceSpace
)
{
throw
new
Error
(
"
XR reference space konnte nicht erstellt werden.
"
);
}
const
viewerSpace
=
await
currentSession
.
requestReferenceSpace
(
"
viewer
"
);
...
...
@@ -1235,7 +860,6 @@ async function activateXR(sceneData = null) {
(
position
)
=>
{
geoLocation
=
{
latitude
:
roundTo
(
position
.
coords
.
latitude
,
5
),
longitude
:
roundTo
(
position
.
coords
.
longitude
,
5
),
};
...
...
@@ -1245,7 +869,7 @@ async function activateXR(sceneData = null) {
// Load saved scene models
// =================================================
if
(
sceneData
)
{
if
(
sceneData
&&
Array
.
isArray
(
sceneData
.
models
)
)
{
sceneData
.
models
.
forEach
((
model
)
=>
{
if
(
model
.
name
)
{
const
modelDefinition
=
Object
.
values
(
models
).
find
(
...
...
@@ -1260,25 +884,27 @@ async function activateXR(sceneData = null) {
const
filePath
=
modelDefinition
.
file
;
const
{
x
,
z
}
=
leafletToThree
(
model
.
lat
,
model
.
lng
);
// ar_overviewmap.js is loaded before user interaction,
// and exposes this conversion function on window.
const
coordinates
=
window
.
leafletToThree
(
model
.
lat
,
model
.
lng
);
const
positionVector
=
new
THREE
.
Vector3
(
x
,
model
.
position
.
y
,
z
);
const
positionVector
=
new
THREE
.
Vector3
(
coordinates
.
x
,
model
.
position
.
y
,
coordinates
.
z
,
);
placeModel
(
filePath
,
positionVector
,
(
placed
)
=>
{
if
(
model
.
rotation
)
{
placed
.
rotation
.
_x
=
model
.
rotation
.
_x
;
placed
.
rotation
.
_y
=
model
.
rotation
.
_y
;
placed
.
rotation
.
_z
=
model
.
rotation
.
_z
;
placed
.
rotation
.
set
(
model
.
rotation
.
_x
??
model
.
rotation
.
x
??
0
,
model
.
rotation
.
_y
??
model
.
rotation
.
y
??
0
,
model
.
rotation
.
_z
??
model
.
rotation
.
z
??
0
,
)
;
}
if
(
model
.
scale
)
{
placed
.
scale
.
x
=
model
.
scale
.
x
;
placed
.
scale
.
y
=
model
.
scale
.
y
;
placed
.
scale
.
z
=
model
.
scale
.
z
;
placed
.
scale
.
set
(
model
.
scale
.
x
,
model
.
scale
.
y
,
model
.
scale
.
z
);
}
});
}
...
...
@@ -1300,61 +926,45 @@ async function activateXR(sceneData = null) {
},
);
}
// ========================================================
// XR render loop
//
Modern Three.js
XR render loop
// ========================================================
currentSession
.
requestAnimationFrame
(
function
onXRFrame
(
time
,
frame
)
{
currentSession
.
requestAnimationFrame
(
onXRFrame
);
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
currentSession
.
renderState
.
baseLayer
.
framebuffer
,
);
const
pose
=
frame
.
getViewerPose
(
referenceSpace
);
if
(
pose
)
{
const
view
=
pose
.
views
[
0
];
const
viewport
=
currentSession
.
renderState
.
baseLayer
.
getViewport
(
view
);
renderer
.
setSize
(
viewport
.
width
,
viewport
.
height
);
camera
.
matrix
.
fromArray
(
view
.
transform
.
matrix
);
camera
.
projectionMatrix
.
fromArray
(
view
.
projectionMatrix
);
camera
.
updateMatrixWorld
(
true
);
renderer
.
setAnimationLoop
((
time
,
frame
)
=>
{
if
(
!
frame
)
{
return
;
}
// =================================================
// Hit testing
// =================================================
// =================================================
=====
// Hit testing
// =================================================
=====
const
hitTestResults
=
frame
.
getHitTestResults
(
hitTestSource
);
const
hitTestResults
=
frame
.
getHitTestResults
(
hitTestSource
);
if
(
hitTestResults
.
length
>
0
)
{
const
hitPose
=
hitTestResults
[
0
].
getPose
(
referenceSpace
);
if
(
hitTestResults
.
length
>
0
)
{
const
hitPose
=
hitTestResults
[
0
].
getPose
(
referenceSpace
);
if
(
hitPose
&&
reticle
)
{
reticle
.
visible
=
true
;
reticle
.
position
.
set
(
hitPose
.
transform
.
position
.
x
,
hitPose
.
transform
.
position
.
y
,
hitPose
.
transform
.
position
.
z
,
);
reticle
.
updateMatrixWorld
(
true
);
}
}
else
if
(
reticle
)
{
reticle
.
visible
=
false
;
}
// =================================================
// Render AR scene
// =================================================
// =================================================
=====
// Render AR scene
// =================================================
=====
renderer
.
render
(
scene
,
camera
);
}
renderer
.
render
(
scene
,
camera
);
});
}
...
...
@@ -1394,4 +1004,29 @@ function playButtonSound() {
soundTimeout
=
false
;
},
200
);
}
}
\ No newline at end of file
}
/* ============================================================
GLOBAL API BRIDGE
------------------------------------------------------------
ES modules have their own scope. The existing HTML UI uses
inline onclick handlers, so expose the existing application
functions explicitly on window.
============================================================ */
window
.
showMenu
=
showMenu
;
window
.
openRotationMenu
=
openRotationMenu
;
window
.
openScaleMenu
=
openScaleMenu
;
window
.
openMoveMenu
=
openMoveMenu
;
window
.
deleteModel
=
deleteModel
;
window
.
completeEditing
=
completeEditing
;
window
.
confirmDelete
=
confirmDelete
;
window
.
exitAR
=
exitAR
;
window
.
confirmExit
=
confirmExit
;
window
.
closeInfoDialog
=
closeInfoDialog
;
window
.
getAllPlacedModels
=
getAllPlacedModels
;
window
.
activateXR
=
activateXR
;
window
.
getXRRenderCamera
=
()
=>
xrRenderer
&&
xrRenderer
.
xr
.
isPresenting
?
xrRenderer
.
xr
.
getCamera
(
camera
)
:
camera
;
public/ar_overviewmap.js
View file @
499216b7
import
*
as
THREE
from
"
three
"
;
let
init
=
false
;
let
mymap
;
let
userIcon
;
...
...
@@ -98,7 +100,7 @@ function init_map() {
// Add currently placed models to map
// ========================================================
const
models
=
getAllPlacedModels
();
const
models
=
window
.
getAllPlacedModels
();
models
.
forEach
((
model
)
=>
{
const
{
lat
,
lng
}
=
threeToLeaflet
(
model
.
position
.
x
,
model
.
position
.
z
);
...
...
@@ -210,7 +212,12 @@ function updateMap() {
}
// PERFORMANCE: Reuse the same Vector3 object.
camera
.
getWorldPosition
(
cameraPosition
);
const
activeCamera
=
typeof
window
.
getXRRenderCamera
===
"
function
"
?
window
.
getXRRenderCamera
()
:
camera
;
activeCamera
.
getWorldPosition
(
cameraPosition
);
// PERFORMANCE: Do not update Leaflet if the user
// has not moved.
...
...
@@ -323,3 +330,9 @@ function roundTo(n, digits) {
return
parseFloat
(
n
);
}
/* Expose map functions needed by the other application modules. */
window
.
init_map
=
init_map
;
window
.
centerOnPlan
=
centerOnPlan
;
window
.
threeToLeaflet
=
threeToLeaflet
;
window
.
leafletToThree
=
leafletToThree
;
public/ar_start.js
View file @
499216b7
...
...
@@ -79,7 +79,6 @@ function showStartScreen() {
startScreen
.
appendChild
(
hftLogo
);
/* ==========================================================
TITLE
========================================================== */
...
...
@@ -190,7 +189,7 @@ function showStartScreen() {
predefinedButton2
=
null
;
// Start AR
activateXR
(
sceneData
);
window
.
activateXR
(
sceneData
);
};
/* ==========================================================
...
...
@@ -198,7 +197,7 @@ function showStartScreen() {
========================================================== */
loadButton
.
onclick
=
()
=>
{
loadSceneFromFile
((
data
,
fileName
)
=>
{
window
.
loadSceneFromFile
((
data
,
fileName
)
=>
{
if
(
data
&&
fileName
)
{
sceneData
=
data
;
sceneFileName
=
fileName
;
...
...
public/index.html
View file @
499216b7
...
...
@@ -9,8 +9,15 @@
<link
rel=
"stylesheet"
href=
"style.css"
>
<script
src=
"https://unpkg.com/three@0.126.0/build/three.js"
></script>
<script
src=
"https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"
></script>
<!-- Three.js (modern ES module setup) -->
<script
type=
"importmap"
>
{
"
imports
"
:
{
"
three
"
:
"
https://unpkg.com/three@0.186.0/build/three.module.js
"
,
"
three/addons/
"
:
"
https://unpkg.com/three@0.186.0/examples/jsm/
"
}
}
</script>
</head>
<body>
...
...
@@ -29,6 +36,7 @@
STANDARDMENÜ
============================================================ -->
<div
id=
"menu-bar"
>
<div
...
...
@@ -299,7 +307,7 @@
============================================================ -->
<div
id=
"app-version"
>
v0.1.
3
v0.1.
186
</div>
...
...
@@ -307,21 +315,22 @@
JAVASCRIPT
============================================================ -->
<script
src=
"ar_start.js"
></script>
<script
src=
"ar_main.js"
></script>
<!-- Debug console loads first so it can capture module errors/logs -->
<script
src=
"ar_debug_console.js"
></script>
<!-- Application modules -->
<script
type=
"module"
src=
"ar_main.js"
></script>
<script
type=
"module"
src=
"ar_overviewmap.js"
></script>
<script
type=
"module"
src=
"ar_download.js"
></script>
<script
type=
"module"
src=
"ar_start.js"
></script>
<link
rel=
"stylesheet"
href=
"https://unpkg.com/leaflet/dist/leaflet.css"
/>
<script
src=
"https://unpkg.com/leaflet/dist/leaflet.js"
></script>
<script
src=
"ar_overviewmap.js"
></script>
<script
src=
"ar_download.js"
></script>
<!-- ============================================================
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment