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
af3bbe7d
Commit
af3bbe7d
authored
Oct 05, 2026
by
Alfakhori
Browse files
return to 126
parent
ae2a0557
Pipeline
#12486
passed with stage
in 26 seconds
Changes
5
Pipelines
1
Show whitespace changes
Inline
Side-by-side
public/ar_download.js
View file @
af3bbe7d
function
downloadScene
()
{
function
downloadScene
()
{
const
placedModels
=
window
.
getAllPlacedModels
().
map
((
model
)
=>
{
const
placedModels
=
getAllPlacedModels
().
map
(
model
=>
{
const
{
lat
,
lng
}
=
window
.
threeToLeaflet
(
const
{
lat
,
lng
}
=
threeToLeaflet
(
model
.
position
.
x
,
model
.
position
.
z
);
model
.
position
.
x
,
model
.
position
.
z
,
);
return
{
return
{
name
:
model
.
modelConfig
.
name
,
name
:
model
.
modelConfig
.
name
,
position
:
model
.
position
,
position
:
model
.
position
,
...
@@ -11,10 +8,10 @@ function downloadScene() {
...
@@ -11,10 +8,10 @@ function downloadScene() {
scale
:
model
.
scale
,
scale
:
model
.
scale
,
lat
:
lat
,
lat
:
lat
,
lng
:
lng
,
lng
:
lng
,
}
;
}
});
});
console
.
log
(
"
Modelle:
"
+
placedModels
.
length
)
;
console
.
log
(
"
Modelle:
"
+
placedModels
.
length
)
if
(
placedModels
.
length
===
0
)
{
if
(
placedModels
.
length
===
0
)
{
showInfoDialog
(
"
Szene ist leer
"
);
showInfoDialog
(
"
Szene ist leer
"
);
return
;
return
;
...
@@ -30,7 +27,7 @@ function downloadScene() {
...
@@ -30,7 +27,7 @@ function downloadScene() {
lat
:
geoLocation
.
latitude
,
lat
:
geoLocation
.
latitude
,
lng
:
geoLocation
.
longitude
,
lng
:
geoLocation
.
longitude
,
models
:
placedModels
,
models
:
placedModels
,
}
;
}
const
sceneData
=
JSON
.
stringify
(
data
,
null
,
1
);
const
sceneData
=
JSON
.
stringify
(
data
,
null
,
1
);
const
blob
=
new
Blob
([
sceneData
],
{
type
:
"
application/json
"
});
const
blob
=
new
Blob
([
sceneData
],
{
type
:
"
application/json
"
});
...
@@ -48,20 +45,20 @@ function downloadScene() {
...
@@ -48,20 +45,20 @@ function downloadScene() {
}
}
function
loadSceneFromFile
(
loadedData
)
{
function
loadSceneFromFile
(
loadedData
)
{
const
fileInput
=
document
.
createElement
(
"
input
"
);
const
fileInput
=
document
.
createElement
(
'
input
'
);
fileInput
.
type
=
"
file
"
;
fileInput
.
type
=
'
file
'
;
fileInput
.
accept
=
"
application/json
"
;
fileInput
.
accept
=
'
application/json
'
;
fileInput
.
addEventListener
(
"
change
"
,
function
(
event
)
{
fileInput
.
addEventListener
(
'
change
'
,
function
(
event
)
{
const
file
=
event
.
target
.
files
[
0
];
const
file
=
event
.
target
.
files
[
0
];
const
fileName
=
file
?
file
.
name
:
"
Keine Datei ausgewählt
"
;
const
fileName
=
file
?
file
.
name
:
"
Keine Datei ausgewählt
"
if
(
!
file
)
{
if
(
!
file
)
{
console
.
log
(
"
Keine Datei ausgewählt
"
);
console
.
log
(
"
Keine Datei ausgewählt
"
);
return
;
return
;
}
}
const
reader
=
new
FileReader
();
const
reader
=
new
FileReader
();
reader
.
onload
=
function
(
e
)
{
reader
.
onload
=
function
(
e
)
{
try
{
try
{
const
sceneData
=
JSON
.
parse
(
e
.
target
.
result
);
const
sceneData
=
JSON
.
parse
(
e
.
target
.
result
);
loadedData
(
sceneData
,
fileName
);
loadedData
(
sceneData
,
fileName
);
...
@@ -74,7 +71,3 @@ function loadSceneFromFile(loadedData) {
...
@@ -74,7 +71,3 @@ function loadSceneFromFile(loadedData) {
});
});
fileInput
.
click
();
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 @
af3bbe7d
import
*
as
THREE
from
"
three
"
;
import
{
GLTFLoader
}
from
"
three/addons/loaders/GLTFLoader.js
"
;
/* ========================= */
/* ========================= */
/* GLOBALE VARIABLEN */
/* GLOBALE VARIABLEN */
/* ========================= */
/* ========================= */
...
@@ -9,43 +6,44 @@ let selectedPlacedModel = null;
...
@@ -9,43 +6,44 @@ let selectedPlacedModel = null;
let
currentSession
=
null
;
let
currentSession
=
null
;
let
reticle
=
null
;
let
reticle
=
null
;
let
scene
,
camera
;
let
scene
,
camera
;
let
xrRenderer
=
null
;
let
geoLocation
;
let
geoLocation
;
const
modelCache
=
new
Map
();
const
modelCache
=
new
Map
();
// PERFORMANCE: Reuse one GLTFLoader instead of creating
// PERFORMANCE: Reuse one GLTFLoader instead of creating
// a new loader every time a model is placed.
// a new loader every time a model is placed.
const
gltfLoader
=
new
GLTFLoader
();
const
gltfLoader
=
new
THREE
.
GLTFLoader
();
const
menus
=
[
const
menus
=
[
"
menu-bar
"
,
'
menu-bar
'
,
"
add-menu
"
,
'
add-menu
'
,
"
edit-menu
"
,
'
edit-menu
'
,
"
options-menu
"
,
'
options-menu
'
,
"
map-window
"
,
'
map-window
'
];
];
// PERFORMANCE: Cache menu DOM elements once instead of
// PERFORMANCE: Cache menu DOM elements once instead of
// repeatedly calling document.getElementById() in showMenu().
// repeatedly calling document.getElementById() in showMenu().
const
menuElements
=
{};
const
menuElements
=
{};
menus
.
forEach
(
(
id
)
=>
{
menus
.
forEach
(
id
=>
{
menuElements
[
id
]
=
document
.
getElementById
(
id
);
menuElements
[
id
]
=
document
.
getElementById
(
id
);
});
});
/* ========================= */
/* ========================= */
/* MODELLE */
/* MODELLE */
/* ========================= */
/* ========================= */
let
models
=
{
let
models
=
{
bench
:
{
bench
:
{
name
:
"
Bench
"
,
name
:
"
Bench
"
,
image
:
"
assets/previewImages/bench.PNG
"
,
image
:
"
assets/previewImages/bench.PNG
"
,
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/bench_model/scene.gltf
"
,
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
},
scale
:
{
x
:
0.1
,
y
:
0.1
,
z
:
0.1
},
minScale
:
0.05
,
minScale
:
0.05
,
maxScale
:
0.5
,
maxScale
:
0.5
},
},
trashbin
:
{
trashbin
:
{
...
@@ -54,7 +52,7 @@ let models = {
...
@@ -54,7 +52,7 @@ let models = {
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/trash_model/scene.gltf
"
,
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
},
scale
:
{
x
:
0.03
,
y
:
0.03
,
z
:
0.03
},
minScale
:
0.01
,
minScale
:
0.01
,
maxScale
:
0.1
,
maxScale
:
0.1
},
},
lantern
:
{
lantern
:
{
...
@@ -63,7 +61,7 @@ let models = {
...
@@ -63,7 +61,7 @@ let models = {
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/park_light_model/scene.gltf
"
,
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
},
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.2
,
minScale
:
0.2
,
maxScale
:
5
,
maxScale
:
5
},
},
telephone_box
:
{
telephone_box
:
{
...
@@ -72,7 +70,7 @@ let models = {
...
@@ -72,7 +70,7 @@ let models = {
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/telephone_box_model/scene.gltf
"
,
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
},
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.05
,
minScale
:
0.05
,
maxScale
:
1
,
maxScale
:
1
},
},
fire_hydrant_model
:
{
fire_hydrant_model
:
{
...
@@ -81,7 +79,7 @@ let models = {
...
@@ -81,7 +79,7 @@ let models = {
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fire_hydrant_model/scene.gltf
"
,
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
},
scale
:
{
x
:
0.3
,
y
:
0.3
,
z
:
0.3
},
minScale
:
0.1
,
minScale
:
0.1
,
maxScale
:
1
,
maxScale
:
1
},
},
statue
:
{
statue
:
{
...
@@ -90,7 +88,7 @@ let models = {
...
@@ -90,7 +88,7 @@ let models = {
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/statue_model/scene.gltf
"
,
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
},
scale
:
{
x
:
0.5
,
y
:
0.5
,
z
:
0.5
},
minScale
:
0.05
,
minScale
:
0.05
,
maxScale
:
2
,
maxScale
:
2
},
},
fountain
:
{
fountain
:
{
...
@@ -99,27 +97,37 @@ let models = {
...
@@ -99,27 +97,37 @@ let models = {
file
:
"
https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fountain_model/scene.gltf
"
,
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
},
scale
:
{
x
:
0.001
,
y
:
0.001
,
z
:
0.001
},
minScale
:
0.0005
,
minScale
:
0.0005
,
maxScale
:
0.005
,
maxScale
:
0.005
}
,
}
};
};
/* ========================= */
/* ========================= */
/* INITIALISIERUNG */
/* INITIALISIERUNG */
/* ========================= */
/* ========================= */
window
.
onload
=
()
=>
{
window
.
onload
=
()
=>
{
initializeAddMenu
();
initializeAddMenu
();
// Allen Buttons den Sound hinzufügen
// Allen Buttons den Sound hinzufügen
const
buttons
=
document
.
querySelectorAll
(
"
button, .menu-item
"
);
const
buttons
=
document
.
querySelectorAll
(
"
button, .menu-item
"
);
buttons
.
forEach
((
button
)
=>
{
buttons
.
forEach
(
button
=>
{
button
.
addEventListener
(
"
click
"
,
playButtonSound
);
button
.
addEventListener
(
"
click
"
,
playButtonSound
);
});
});
};
};
function
initializeAddMenu
()
{
function
initializeAddMenu
()
{
const
addMenu
=
document
.
getElementById
(
"
add-menu
"
);
const
addMenu
=
document
.
getElementById
(
'
add-menu
'
);
addMenu
.
innerHTML
=
addMenu
.
innerHTML
=
Object
.
entries
(
models
)
Object
.
entries
(
models
)
...
@@ -135,9 +143,10 @@ function initializeAddMenu() {
...
@@ -135,9 +143,10 @@ function initializeAddMenu() {
alt="
${
model
.
name
}
"
alt="
${
model
.
name
}
"
/>
/>
</div>
</div>
`
,
`
)
)
.
join
(
""
)
+
.
join
(
''
)
+
`
`
<div
<div
class="menu-item"
class="menu-item"
...
@@ -151,183 +160,300 @@ function initializeAddMenu() {
...
@@ -151,183 +160,300 @@ function initializeAddMenu() {
`
;
`
;
}
}
/* ========================= */
/* ========================= */
/* MENÜ-STEUERUNG */
/* MENÜ-STEUERUNG */
/* ========================= */
/* ========================= */
function
showMenu
(
menuId
)
{
function
showMenu
(
menuId
)
{
const
isMapWindow
=
menuId
===
"
map-window
"
;
const
isMapWindow
=
menuId
===
'
map-window
'
;
// Check location before opening map
// Check location before opening map
if
(
isMapWindow
&&
!
geoLocation
)
{
if
(
isMapWindow
&&
!
geoLocation
)
{
console
.
log
(
"
Standort nicht geladen
"
);
console
.
log
(
"
Standort nicht geladen
"
);
showInfoDialog
(
"
Ihr Standort wurde noch nicht geladen
"
);
showInfoDialog
(
"
Ihr Standort wurde noch nicht geladen
"
);
return
;
return
;
}
}
// PERFORMANCE: Use cached menu elements instead of
// PERFORMANCE: Use cached menu elements instead of
// repeatedly querying the DOM.
// repeatedly querying the DOM.
menus
.
forEach
((
id
)
=>
{
menus
.
forEach
(
id
=>
{
const
element
=
menuElements
[
id
];
const
element
=
menuElements
[
id
];
if
(
element
)
{
if
(
element
)
{
element
.
style
.
display
=
id
===
menuId
?
"
flex
"
:
"
none
"
;
element
.
style
.
display
=
id
===
menuId
?
'
flex
'
:
'
none
'
;
}
}
});
});
closeDynamicMenu
();
closeDynamicMenu
();
// Menu-specific actions
// Menu-specific actions
if
(
menuId
===
"
menu-bar
"
)
{
if
(
menuId
===
'
menu-bar
'
)
{
clearSelectedModel
();
clearSelectedModel
();
}
else
if
(
isMapWindow
)
{
}
else
if
(
isMapWindow
)
{
// Initialize map
// Initialize map
init_map
();
init_map
();
// Leaflet needs to know the actual size of the
// Leaflet needs to know the actual size of the
// visible map container.
// visible map container.
setTimeout
(()
=>
{
setTimeout
(()
=>
{
if
(
typeof
centerOnPlan
===
"
function
"
)
{
if
(
typeof
centerOnPlan
===
'
function
'
)
{
centerOnPlan
();
centerOnPlan
();
}
}
},
100
);
},
100
);
}
}
}
}
/* ============================================================ */
/* ============================================================ */
/* Close map and return to AR scene */
/* Close map and return to AR scene */
/* ============================================================ */
/* ============================================================ */
function
closeMap
()
{
function
closeMap
()
{
const
mapWindow
=
menuElements
[
"
map-window
"
];
const
mapWindow
=
menuElements
[
'
map-window
'
];
if
(
mapWindow
)
{
if
(
mapWindow
)
{
mapWindow
.
style
.
display
=
"
none
"
;
mapWindow
.
style
.
display
=
"
none
"
;
}
}
// Show the main AR menu again
// Show the main AR menu again
const
menuBar
=
menuElements
[
"
menu-bar
"
];
const
menuBar
=
menuElements
[
'
menu-bar
'
];
if
(
menuBar
)
{
if
(
menuBar
)
{
menuBar
.
style
.
display
=
"
flex
"
;
menuBar
.
style
.
display
=
"
flex
"
;
}
}
// Stop the map update interval
// Stop the map update interval
if
(
typeof
intervalId
!==
"
undefined
"
)
{
if
(
typeof
intervalId
!==
'
undefined
'
)
{
if
(
intervalId
)
{
if
(
intervalId
)
{
clearInterval
(
intervalId
);
clearInterval
(
intervalId
);
intervalId
=
null
;
intervalId
=
null
;
}
}
}
}
// Clear selected model
// Clear selected model
clearSelectedModel
();
clearSelectedModel
();
}
}
function
closeDynamicMenu
()
{
function
closeDynamicMenu
()
{
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
if
(
dynamicMenu
)
{
if
(
dynamicMenu
)
{
dynamicMenu
.
style
.
display
=
"
none
"
;
dynamicMenu
.
style
.
display
=
"
none
"
;
}
}
}
}
function
showInfoDialog
(
message
)
{
function
showInfoDialog
(
message
)
{
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
const
infoText
=
document
.
getElementById
(
"
info-text
"
);
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
infoText
.
textContent
=
message
;
const
infoText
=
document
.
getElementById
(
"
info-text
"
);
infoText
.
textContent
=
message
;
infoDialog
.
style
.
display
=
"
flex
"
;
infoDialog
.
style
.
display
=
"
flex
"
;
}
}
function
closeInfoDialog
()
{
function
closeInfoDialog
()
{
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
infoDialog
.
style
.
display
=
"
none
"
;
const
infoDialog
=
document
.
getElementById
(
"
info-dialog
"
);
infoDialog
.
style
.
display
=
"
none
"
;
}
}
/* ========================= */
/* ========================= */
/* MODELL-HANDLING */
/* MODELL-HANDLING */
/* ========================= */
/* ========================= */
function
getAllPlacedModels
()
{
function
getAllPlacedModels
()
{
return
scene
.
children
.
filter
((
child
)
=>
child
.
isPlacedModel
===
true
);
return
scene
.
children
.
filter
(
child
=>
child
.
isPlacedModel
===
true
);
}
}
function
loadModel
(
filePath
)
{
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
(
function
placeModel
(
(
model
)
=>
model
.
file
===
filePath
,
filePath
,
position
,
onPlace
=
null
)
{
const
modelConfig
=
Object
.
values
(
models
)
.
find
(
model
=>
model
.
file
===
filePath
);
);
// PERFORMANCE: Reuse the global GLTFLoader.
// PERFORMANCE: Reuse the global GLTFLoader.
// Do not create a new loader for every model.
// Do not create a new loader for every model.
if
(
modelCache
.
has
(
filePath
))
{
if
(
modelCache
.
has
(
filePath
))
{
// Use the already loaded model
// Use the already loaded model
const
cachedModel
=
modelCache
.
get
(
filePath
);
const
cachedModel
=
modelCache
.
get
(
filePath
);
const
model
=
cachedModel
.
clone
(
true
);
const
model
=
cachedModel
.
clone
(
true
);
if
(
modelConfig
&&
modelConfig
.
scale
)
{
if
(
modelConfig
&&
modelConfig
.
scale
)
{
model
.
scale
.
set
(
model
.
scale
.
set
(
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
z
,
modelConfig
.
scale
.
z
);
);
}
}
model
.
position
.
copy
(
position
);
model
.
isPlacedModel
=
true
;
model
.
position
.
copy
(
position
);
model
.
isPlacedModel
=
true
;
model
.
modelConfig
=
modelConfig
;
model
.
modelConfig
=
modelConfig
;
scene
.
add
(
model
);
scene
.
add
(
model
);
if
(
onPlace
)
{
if
(
onPlace
)
{
onPlace
(
model
);
onPlace
(
model
);
}
}
}
else
{
}
else
{
// Model has not been loaded yet
// Model has not been loaded yet
gltfLoader
.
load
(
gltfLoader
.
load
(
filePath
,
filePath
,
(
gltf
)
=>
{
(
gltf
)
=>
{
// Store the original model in the cache
// Store the original model in the cache
modelCache
.
set
(
filePath
,
gltf
.
scene
);
modelCache
.
set
(
filePath
,
gltf
.
scene
);
const
model
=
gltf
.
scene
.
clone
(
true
);
const
model
=
gltf
.
scene
.
clone
(
true
);
if
(
modelConfig
&&
modelConfig
.
scale
)
{
if
(
modelConfig
&&
modelConfig
.
scale
)
{
model
.
scale
.
set
(
model
.
scale
.
set
(
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
x
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
y
,
modelConfig
.
scale
.
z
,
modelConfig
.
scale
.
z
);
);
}
}
model
.
position
.
copy
(
position
);
model
.
isPlacedModel
=
true
;
model
.
position
.
copy
(
position
);
model
.
isPlacedModel
=
true
;
model
.
modelConfig
=
modelConfig
;
model
.
modelConfig
=
modelConfig
;
scene
.
add
(
model
);
scene
.
add
(
model
);
if
(
onPlace
)
{
if
(
onPlace
)
{
onPlace
(
model
);
onPlace
(
model
);
}
}
...
@@ -336,164 +462,313 @@ function placeModel(filePath, position, onPlace = null) {
...
@@ -336,164 +462,313 @@ function placeModel(filePath, position, onPlace = null) {
undefined
,
undefined
,
(
error
)
=>
{
(
error
)
=>
{
console
.
error
(
"
Fehler beim Laden des Modells:
"
,
error
);
},
console
.
error
(
"
Fehler beim Laden des Modells:
"
,
error
);
}
);
);
}
}
}
}
function
selectModel
(
modelId
)
{
function
selectModel
(
modelId
)
{
const
model
=
models
[
modelId
];
if
(
model
&&
model
.
file
)
{
const
model
=
loadModel
(
model
.
file
);
models
[
modelId
];
showMenu
(
"
menu-bar
"
);
if
(
model
&&
model
.
file
)
{
loadModel
(
model
.
file
);
showMenu
(
'
menu-bar
'
);
}
}
}
}
function
selectModelFromScene
(
event
)
{
function
selectModelFromScene
(
event
)
{
const
mouse
=
new
THREE
.
Vector2
(
(
event
.
clientX
/
window
.
innerWidth
)
*
2
-
1
,
-
(
event
.
clientY
/
window
.
innerHeight
)
*
2
+
1
,
const
mouse
=
new
THREE
.
Vector2
(
(
event
.
clientX
/
window
.
innerWidth
)
*
2
-
1
,
-
(
event
.
clientY
/
window
.
innerHeight
)
*
2
+
1
);
);
const
raycaster
=
new
THREE
.
Raycaster
();
const
rayCamera
=
const
raycaster
=
xrRenderer
&&
xrRenderer
.
xr
.
isPresenting
new
THREE
.
Raycaster
();
?
xrRenderer
.
xr
.
getCamera
(
camera
)
:
camera
;
raycaster
.
setFromCamera
(
mouse
,
rayCamera
);
const
intersects
=
raycaster
.
intersectObjects
(
scene
.
children
,
true
);
raycaster
.
setFromCamera
(
mouse
,
camera
);
if
(
intersects
.
length
>
0
)
{
let
selectedObject
=
intersects
[
0
].
object
;
while
(
selectedObject
.
parent
&&
selectedObject
.
parent
!==
scene
)
{
const
intersects
=
selectedObject
=
selectedObject
.
parent
;
raycaster
.
intersectObjects
(
scene
.
children
,
true
);
if
(
intersects
.
length
>
0
)
{
let
selectedObject
=
intersects
[
0
].
object
;
while
(
selectedObject
.
parent
&&
selectedObject
.
parent
!==
scene
)
{
selectedObject
=
selectedObject
.
parent
;
}
}
// Reticle cannot be selected
// Reticle cannot be selected
if
(
selectedObject
===
reticle
)
{
if
(
console
.
log
(
"
Reticle kann nicht ausgewählt werden.
"
);
selectedObject
===
reticle
)
{
console
.
log
(
"
Reticle kann nicht ausgewählt werden.
"
);
return
;
return
;
}
}
selectedPlacedModel
=
selectedObject
;
selectedPlacedModel
=
selectedObject
;
highlightSelectedModel
();
highlightSelectedModel
();
showMenu
(
"
edit-menu
"
);
showMenu
(
"
edit-menu
"
);
}
}
}
}
function
clearSelectedModel
()
{
function
clearSelectedModel
()
{
if
(
selectedPlacedModel
)
{
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
traverse
((
child
)
=>
{
selectedPlacedModel
.
traverse
(
(
child
)
=>
{
if
(
child
.
isMesh
)
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
}
});
}
);
selectedPlacedModel
=
null
;
selectedPlacedModel
=
null
;
}
}
}
}
function
highlightSelectedModel
()
{
function
highlightSelectedModel
()
{
if
(
selectedPlacedModel
)
{
if
(
selectedPlacedModel
)
{
removeHighlightFromAllModels
();
removeHighlightFromAllModels
();
selectedPlacedModel
.
traverse
((
child
)
=>
{
selectedPlacedModel
.
traverse
(
(
child
)
=>
{
if
(
child
.
isMesh
)
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0xff0000
);
child
.
material
.
emissive
.
setHex
(
0xff0000
);
}
}
});
}
);
}
}
}
}
function
removeHighlightFromSelectedModel
()
{
function
removeHighlightFromSelectedModel
()
{
if
(
selectedPlacedModel
)
{
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
traverse
((
child
)
=>
{
selectedPlacedModel
.
traverse
(
(
child
)
=>
{
if
(
child
.
isMesh
)
{
if
(
child
.
isMesh
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
}
});
}
);
}
}
}
}
function
removeHighlightFromAllModels
()
{
function
removeHighlightFromAllModels
()
{
scene
.
traverse
((
child
)
=>
{
if
(
child
.
isMesh
&&
child
.
material
&&
child
.
material
.
emissive
)
{
scene
.
traverse
(
child
.
material
.
emissive
.
setHex
(
0x000000
);
(
child
)
=>
{
if
(
child
.
isMesh
&&
child
.
material
&&
child
.
material
.
emissive
)
{
child
.
material
.
emissive
.
setHex
(
0x000000
);
}
}
});
}
);
}
}
function
deleteModel
()
{
function
deleteModel
()
{
if
(
!
selectedPlacedModel
)
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.
"
,
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.
"
);
);
return
;
return
;
}
}
const
deleteDialog
=
document
.
getElementById
(
"
delete-confirmation-dialog
"
);
deleteDialog
.
style
.
display
=
"
flex
"
;
const
deleteDialog
=
document
.
getElementById
(
'
delete-confirmation-dialog
'
);
deleteDialog
.
style
.
display
=
'
flex
'
;
}
}
function
confirmDelete
(
shouldDelete
)
{
const
deleteDialog
=
document
.
getElementById
(
"
delete-confirmation-dialog
"
);
deleteDialog
.
style
.
display
=
"
none
"
;
function
confirmDelete
(
shouldDelete
)
{
const
deleteDialog
=
document
.
getElementById
(
'
delete-confirmation-dialog
'
);
deleteDialog
.
style
.
display
=
'
none
'
;
if
(
shouldDelete
&&
selectedPlacedModel
)
{
if
(
scene
.
remove
(
selectedPlacedModel
);
shouldDelete
&&
selectedPlacedModel
)
{
scene
.
remove
(
selectedPlacedModel
);
selectedPlacedModel
=
null
;
selectedPlacedModel
=
null
;
showMenu
(
"
menu-bar
"
);
showMenu
(
'
menu-bar
'
);
}
}
}
}
function
completeEditing
()
{
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 */
/* BEARBEITUNGS-MENÜS */
/* ========================= */
/* ========================= */
function
openRotationMenu
()
{
function
openRotationMenu
()
{
if
(
!
selectedPlacedModel
)
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
,
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
);
);
return
;
return
;
}
}
const
currentRotation
=
Math
.
round
(
THREE
.
MathUtils
.
radToDeg
(
selectedPlacedModel
.
rotation
.
y
),
const
currentRotation
=
Math
.
round
(
THREE
.
MathUtils
.
radToDeg
(
selectedPlacedModel
.
rotation
.
y
)
);
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
);
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
dynamicMenu
.
style
.
display
=
"
flex
"
;
dynamicMenu
.
innerHTML
=
`
dynamicMenu
.
innerHTML
=
`
...
@@ -520,43 +795,83 @@ function openRotationMenu() {
...
@@ -520,43 +795,83 @@ function openRotationMenu() {
`
;
`
;
}
}
function
updateRotation
(
axis
,
value
)
{
function
updateRotation
(
axis
,
value
)
{
if
(
selectedPlacedModel
)
{
if
(
selectedPlacedModel
)
{
const
radians
=
(
value
/
180
)
*
Math
.
PI
;
selectedPlacedModel
.
rotation
[
axis
]
=
radians
;
const
radians
=
(
value
/
180
)
*
Math
.
PI
;
selectedPlacedModel
.
rotation
[
axis
]
=
radians
;
const
currentRotationDisplay
=
document
.
getElementById
(
"
current-rotation
"
);
const
currentRotationDisplay
=
document
.
getElementById
(
"
current-rotation
"
);
if
(
currentRotationDisplay
)
{
if
(
currentRotationDisplay
)
{
currentRotationDisplay
.
textContent
=
value
;
currentRotationDisplay
.
textContent
=
value
;
}
}
}
}
}
}
function
openScaleMenu
()
{
function
openScaleMenu
()
{
if
(
!
selectedPlacedModel
)
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
,
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
);
);
return
;
return
;
}
}
const
currentScale
=
selectedPlacedModel
.
scale
.
x
;
const
minScale
=
selectedPlacedModel
.
modelConfig
.
minScale
;
const
currentScale
=
selectedPlacedModel
.
scale
.
x
;
const
minScale
=
selectedPlacedModel
.
modelConfig
.
minScale
;
const
maxScale
=
selectedPlacedModel
.
modelConfig
.
maxScale
;
const
maxScale
=
selectedPlacedModel
.
modelConfig
.
maxScale
;
const
step
=
(
maxScale
-
minScale
)
/
100
;
const
step
=
(
maxScale
-
minScale
)
/
100
;
const
currentScalePercent
=
const
currentScalePercent
=
((
currentScale
-
minScale
)
/
(
maxScale
-
minScale
))
*
100
;
(
(
currentScale
-
minScale
)
/
(
maxScale
-
minScale
)
)
*
100
;
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
dynamicMenu
.
innerHTML
=
`
dynamicMenu
.
innerHTML
=
`
...
@@ -588,34 +903,67 @@ function openScaleMenu() {
...
@@ -588,34 +903,67 @@ function openScaleMenu() {
`
;
`
;
}
}
function
updateScale
(
percentValue
,
minScale
,
maxScale
)
{
function
updateScale
(
percentValue
,
minScale
,
maxScale
)
{
if
(
selectedPlacedModel
)
{
if
(
selectedPlacedModel
)
{
const
scale
=
minScale
+
(
percentValue
/
100
)
*
(
maxScale
-
minScale
);
selectedPlacedModel
.
scale
.
set
(
scale
,
scale
,
scale
);
const
scale
=
minScale
+
(
percentValue
/
100
)
*
(
maxScale
-
minScale
);
selectedPlacedModel
.
scale
.
set
(
scale
,
scale
,
scale
);
const
scaleValueDisplay
=
document
.
getElementById
(
"
scale-value
"
);
const
scaleValueDisplay
=
document
.
getElementById
(
"
scale-value
"
);
if
(
scaleValueDisplay
)
{
if
(
scaleValueDisplay
)
{
scaleValueDisplay
.
textContent
=
`
${
parseInt
(
percentValue
,
10
)}
%`
;
scaleValueDisplay
.
textContent
=
`
${
parseInt
(
percentValue
,
10
)}
%`
;
}
}
}
}
}
}
let
moveDelta
=
0.1
;
let
moveDelta
=
0.1
;
function
openMoveMenu
()
{
function
openMoveMenu
()
{
if
(
!
selectedPlacedModel
)
{
if
(
!
selectedPlacedModel
)
{
console
.
log
(
console
.
log
(
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
,
"
Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.
"
);
);
return
;
return
;
}
}
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
const
dynamicMenu
=
document
.
getElementById
(
"
dynamic-menu
"
);
dynamicMenu
.
style
.
display
=
"
flex
"
;
dynamicMenu
.
innerHTML
=
`
dynamicMenu
.
innerHTML
=
`
...
@@ -696,38 +1044,70 @@ function openMoveMenu() {
...
@@ -696,38 +1044,70 @@ function openMoveMenu() {
`
;
`
;
}
}
function
updateMoveDelta
(
value
)
{
moveDelta
=
parseFloat
(
value
);
const
moveDeltaDisplay
=
document
.
getElementById
(
"
move-delta-display
"
);
function
updateMoveDelta
(
value
)
{
moveDelta
=
parseFloat
(
value
);
const
moveDeltaDisplay
=
document
.
getElementById
(
"
move-delta-display
"
);
if
(
moveDeltaDisplay
)
{
if
(
moveDeltaDisplay
)
{
moveDeltaDisplay
.
textContent
=
moveDelta
.
toFixed
(
2
);
moveDeltaDisplay
.
textContent
=
moveDelta
.
toFixed
(
2
);
}
}
}
}
function
moveModelDynamic
(
axis
,
value
)
{
function
moveModelDynamic
(
axis
,
value
)
{
if
(
selectedPlacedModel
)
{
if
(
selectedPlacedModel
)
{
selectedPlacedModel
.
position
[
axis
]
+=
value
;
selectedPlacedModel
.
position
[
axis
]
+=
value
;
}
}
}
}
/* ========================= */
/* ========================= */
/* KARTENSTEUERUNG */
/* KARTENSTEUERUNG */
/* ========================= */
/* ========================= */
function
refreshMapDialog
()
{
function
refreshMapDialog
()
{
const
mapDialog
=
document
.
getElementById
(
"
map-dialog
"
);
mapDialog
.
style
.
display
=
"
flex
"
;
const
mapDialog
=
document
.
getElementById
(
'
map-dialog
'
);
mapDialog
.
style
.
display
=
'
flex
'
;
}
}
function
closeMapDialog
()
{
function
closeMapDialog
()
{
const
mapDialog
=
document
.
getElementById
(
"
map-dialog
"
);
mapDialog
.
style
.
display
=
"
none
"
;
const
mapDialog
=
document
.
getElementById
(
'
map-dialog
'
);
mapDialog
.
style
.
display
=
'
none
'
;
}
}
/* ========================= */
/* ========================= */
/* AR-HANDLING */
/* AR-HANDLING */
/* ========================= */
/* ========================= */
...
@@ -737,62 +1117,60 @@ async function activateXR(sceneData = null) {
...
@@ -737,62 +1117,60 @@ async function activateXR(sceneData = null) {
document
.
body
.
appendChild
(
canvas
);
document
.
body
.
appendChild
(
canvas
);
// ========================================================
const
gl
=
canvas
.
getContext
(
"
webgl
"
,
{
// Three.js WebXR renderer
xrCompatible
:
true
,
// ========================================================
});
const
renderer
=
new
THREE
.
WebGLRenderer
({
const
renderer
=
new
THREE
.
WebGLRenderer
({
alpha
:
true
,
alpha
:
true
,
antialias
:
true
,
canvas
,
canvas
,
context
:
gl
,
});
});
renderer
.
autoClear
=
false
;
renderer
.
autoClear
=
false
;
renderer
.
xr
.
enabled
=
true
;
xrRenderer
=
renderer
;
// Keep the same reference-space type used by the previous
// manual WebXR implementation.
renderer
.
xr
.
setReferenceSpaceType
(
"
local
"
);
scene
=
new
THREE
.
Scene
();
scene
=
new
THREE
.
Scene
();
camera
=
new
THREE
.
PerspectiveCamera
();
camera
=
new
THREE
.
PerspectiveCamera
();
camera
.
matrixAutoUpdate
=
false
;
// ========================================================
// ========================================================
// AR lighting
// AR lighting
// ========================================================
// ========================================================
// Soft overall illumination
const
ambientLight
=
new
THREE
.
AmbientLight
(
0xffffff
,
2.5
);
const
ambientLight
=
new
THREE
.
AmbientLight
(
0xffffff
,
2.5
);
scene
.
add
(
ambientLight
);
scene
.
add
(
ambientLight
);
// Sky/ground illumination
const
hemisphereLight
=
new
THREE
.
HemisphereLight
(
0xffffff
,
0x888888
,
1.5
);
const
hemisphereLight
=
new
THREE
.
HemisphereLight
(
0xffffff
,
0x888888
,
1.5
);
scene
.
add
(
hemisphereLight
);
scene
.
add
(
hemisphereLight
);
// Directional light for shadows and form
const
directionalLight
=
new
THREE
.
DirectionalLight
(
0xffffff
,
1.5
);
const
directionalLight
=
new
THREE
.
DirectionalLight
(
0xffffff
,
1.5
);
directionalLight
.
position
.
set
(
10
,
20
,
10
);
directionalLight
.
position
.
set
(
10
,
20
,
10
);
scene
.
add
(
directionalLight
);
scene
.
add
(
directionalLight
);
// ========================================================
// ========================================================
// Reticle
// Reticle
// ========================================================
// ========================================================
const
loader
=
new
GLTFLoader
();
const
loader
=
new
THREE
.
GLTFLoader
();
loader
.
load
(
loader
.
load
(
"
https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf
"
,
"
https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf
"
,
(
gltf
)
=>
{
(
gltf
)
=>
{
reticle
=
gltf
.
scene
;
reticle
=
gltf
.
scene
;
reticle
.
visible
=
false
;
scene
.
add
(
reticle
);
},
undefined
,
reticle
.
visible
=
false
;
(
error
)
=>
{
scene
.
add
(
reticle
);
console
.
error
(
"
Fehler beim Laden des Reticles:
"
,
error
);
},
},
);
);
...
@@ -810,14 +1188,11 @@ async function activateXR(sceneData = null) {
...
@@ -810,14 +1188,11 @@ async function activateXR(sceneData = null) {
requiredFeatures
:
[
"
hit-test
"
],
requiredFeatures
:
[
"
hit-test
"
],
});
});
// Let Three.js manage the XR render state / framebuffer.
currentSession
.
updateRenderState
({
await
renderer
.
xr
.
setSession
(
currentSession
);
baseLayer
:
new
XRWebGLLayer
(
currentSession
,
gl
),
});
const
referenceSpace
=
renderer
.
xr
.
getReferenceSpace
();
const
referenceSpace
=
await
currentSession
.
requestReferenceSpace
(
"
local
"
);
if
(
!
referenceSpace
)
{
throw
new
Error
(
"
XR reference space konnte nicht erstellt werden.
"
);
}
const
viewerSpace
=
await
currentSession
.
requestReferenceSpace
(
"
viewer
"
);
const
viewerSpace
=
await
currentSession
.
requestReferenceSpace
(
"
viewer
"
);
...
@@ -859,8 +1234,9 @@ async function activateXR(sceneData = null) {
...
@@ -859,8 +1234,9 @@ async function activateXR(sceneData = null) {
navigator
.
geolocation
.
getCurrentPosition
(
navigator
.
geolocation
.
getCurrentPosition
(
(
position
)
=>
{
(
position
)
=>
{
geoLocation
=
{
geoLocation
=
{
latitude
:
window
.
roundTo
(
position
.
coords
.
latitude
,
5
),
latitude
:
roundTo
(
position
.
coords
.
latitude
,
5
),
longitude
:
window
.
roundTo
(
position
.
coords
.
longitude
,
5
),
longitude
:
roundTo
(
position
.
coords
.
longitude
,
5
),
};
};
console
.
log
(
"
GeoLocation:
"
+
JSON
.
stringify
(
geoLocation
));
console
.
log
(
"
GeoLocation:
"
+
JSON
.
stringify
(
geoLocation
));
...
@@ -869,7 +1245,7 @@ async function activateXR(sceneData = null) {
...
@@ -869,7 +1245,7 @@ async function activateXR(sceneData = null) {
// Load saved scene models
// Load saved scene models
// =================================================
// =================================================
if
(
sceneData
&&
Array
.
isArray
(
sceneData
.
models
)
)
{
if
(
sceneData
)
{
sceneData
.
models
.
forEach
((
model
)
=>
{
sceneData
.
models
.
forEach
((
model
)
=>
{
if
(
model
.
name
)
{
if
(
model
.
name
)
{
const
modelDefinition
=
Object
.
values
(
models
).
find
(
const
modelDefinition
=
Object
.
values
(
models
).
find
(
...
@@ -884,27 +1260,25 @@ async function activateXR(sceneData = null) {
...
@@ -884,27 +1260,25 @@ async function activateXR(sceneData = null) {
const
filePath
=
modelDefinition
.
file
;
const
filePath
=
modelDefinition
.
file
;
// ar_overviewmap.js is loaded before user interaction,
const
{
x
,
z
}
=
leafletToThree
(
model
.
lat
,
model
.
lng
);
// and exposes this conversion function on window.
const
coordinates
=
window
.
leafletToThree
(
model
.
lat
,
model
.
lng
);
const
positionVector
=
new
THREE
.
Vector3
(
const
positionVector
=
new
THREE
.
Vector3
(
x
,
model
.
position
.
y
,
z
);
coordinates
.
x
,
model
.
position
.
y
,
coordinates
.
z
,
);
placeModel
(
filePath
,
positionVector
,
(
placed
)
=>
{
placeModel
(
filePath
,
positionVector
,
(
placed
)
=>
{
if
(
model
.
rotation
)
{
if
(
model
.
rotation
)
{
placed
.
rotation
.
set
(
placed
.
rotation
.
_x
=
model
.
rotation
.
_x
;
model
.
rotation
.
_x
??
model
.
rotation
.
x
??
0
,
model
.
rotation
.
_y
??
model
.
rotation
.
y
??
0
,
placed
.
rotation
.
_y
=
model
.
rotation
.
_y
;
model
.
rotation
.
_z
??
model
.
rotation
.
z
??
0
,
)
;
placed
.
rotation
.
_z
=
model
.
rotation
.
_z
;
}
}
if
(
model
.
scale
)
{
if
(
model
.
scale
)
{
placed
.
scale
.
set
(
model
.
scale
.
x
,
model
.
scale
.
y
,
model
.
scale
.
z
);
placed
.
scale
.
x
=
model
.
scale
.
x
;
placed
.
scale
.
y
=
model
.
scale
.
y
;
placed
.
scale
.
z
=
model
.
scale
.
z
;
}
}
});
});
}
}
...
@@ -926,45 +1300,61 @@ async function activateXR(sceneData = null) {
...
@@ -926,45 +1300,61 @@ async function activateXR(sceneData = null) {
},
},
);
);
}
}
// ========================================================
// ========================================================
//
Modern Three.js
XR render loop
// XR render loop
// ========================================================
// ========================================================
renderer
.
setAnimationLoop
((
time
,
frame
)
=>
{
currentSession
.
requestAnimationFrame
(
function
onXRFrame
(
time
,
frame
)
{
if
(
!
frame
)
{
currentSession
.
requestAnimationFrame
(
onXRFrame
);
return
;
}
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
);
// =================================================
// Hit testing
// Hit testing
// =================================================
=====
// =================================================
const
hitTestResults
=
frame
.
getHitTestResults
(
hitTestSource
);
const
hitTestResults
=
frame
.
getHitTestResults
(
hitTestSource
);
if
(
hitTestResults
.
length
>
0
)
{
if
(
hitTestResults
.
length
>
0
)
{
const
hitPose
=
hitTestResults
[
0
].
getPose
(
referenceSpace
);
const
hitPose
=
hitTestResults
[
0
].
getPose
(
referenceSpace
);
if
(
hitPose
&&
reticle
)
{
reticle
.
visible
=
true
;
reticle
.
visible
=
true
;
reticle
.
position
.
set
(
reticle
.
position
.
set
(
hitPose
.
transform
.
position
.
x
,
hitPose
.
transform
.
position
.
x
,
hitPose
.
transform
.
position
.
y
,
hitPose
.
transform
.
position
.
y
,
hitPose
.
transform
.
position
.
z
,
hitPose
.
transform
.
position
.
z
,
);
);
reticle
.
updateMatrixWorld
(
true
);
reticle
.
updateMatrixWorld
(
true
);
}
}
}
else
if
(
reticle
)
{
reticle
.
visible
=
false
;
}
// =================================================
=====
// =================================================
// Render AR scene
// Render AR scene
// =================================================
=====
// =================================================
renderer
.
render
(
scene
,
camera
);
renderer
.
render
(
scene
,
camera
);
}
});
});
}
}
...
@@ -1005,36 +1395,3 @@ function playButtonSound() {
...
@@ -1005,36 +1395,3 @@ function playButtonSound() {
},
200
);
},
200
);
}
}
}
}
\ No newline at end of file
window
.
selectModel
=
selectModel
;
window
.
updateRotation
=
updateRotation
;
window
.
updateScale
=
updateScale
;
window
.
updateMoveDelta
=
updateMoveDelta
;
window
.
moveModelDynamic
=
moveModelDynamic
;
window
.
closeDynamicMenu
=
closeDynamicMenu
;
window
.
roundTo
=
window
.
roundTo
;
/* ============================================================
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 @
af3bbe7d
import
*
as
THREE
from
"
three
"
;
let
init
=
false
;
let
init
=
false
;
let
mymap
;
let
mymap
;
let
userIcon
;
let
userIcon
;
...
@@ -100,7 +98,7 @@ function init_map() {
...
@@ -100,7 +98,7 @@ function init_map() {
// Add currently placed models to map
// Add currently placed models to map
// ========================================================
// ========================================================
const
models
=
window
.
getAllPlacedModels
();
const
models
=
getAllPlacedModels
();
models
.
forEach
((
model
)
=>
{
models
.
forEach
((
model
)
=>
{
const
{
lat
,
lng
}
=
threeToLeaflet
(
model
.
position
.
x
,
model
.
position
.
z
);
const
{
lat
,
lng
}
=
threeToLeaflet
(
model
.
position
.
x
,
model
.
position
.
z
);
...
@@ -212,12 +210,7 @@ function updateMap() {
...
@@ -212,12 +210,7 @@ function updateMap() {
}
}
// PERFORMANCE: Reuse the same Vector3 object.
// PERFORMANCE: Reuse the same Vector3 object.
const
activeCamera
=
camera
.
getWorldPosition
(
cameraPosition
);
typeof
window
.
getXRRenderCamera
===
"
function
"
?
window
.
getXRRenderCamera
()
:
camera
;
activeCamera
.
getWorldPosition
(
cameraPosition
);
// PERFORMANCE: Do not update Leaflet if the user
// PERFORMANCE: Do not update Leaflet if the user
// has not moved.
// has not moved.
...
@@ -330,10 +323,3 @@ function roundTo(n, digits) {
...
@@ -330,10 +323,3 @@ function roundTo(n, digits) {
return
parseFloat
(
n
);
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
;
window
.
roundTo
=
roundTo
;
public/ar_start.js
View file @
af3bbe7d
...
@@ -79,6 +79,7 @@ function showStartScreen() {
...
@@ -79,6 +79,7 @@ function showStartScreen() {
startScreen
.
appendChild
(
hftLogo
);
startScreen
.
appendChild
(
hftLogo
);
/* ==========================================================
/* ==========================================================
TITLE
TITLE
========================================================== */
========================================================== */
...
@@ -189,7 +190,7 @@ function showStartScreen() {
...
@@ -189,7 +190,7 @@ function showStartScreen() {
predefinedButton2
=
null
;
predefinedButton2
=
null
;
// Start AR
// Start AR
window
.
window
.
activateXR
(
sceneData
);
activateXR
(
sceneData
);
};
};
/* ==========================================================
/* ==========================================================
...
@@ -197,7 +198,7 @@ function showStartScreen() {
...
@@ -197,7 +198,7 @@ function showStartScreen() {
========================================================== */
========================================================== */
loadButton
.
onclick
=
()
=>
{
loadButton
.
onclick
=
()
=>
{
window
.
window
.
loadSceneFromFile
((
data
,
fileName
)
=>
{
loadSceneFromFile
((
data
,
fileName
)
=>
{
if
(
data
&&
fileName
)
{
if
(
data
&&
fileName
)
{
sceneData
=
data
;
sceneData
=
data
;
sceneFileName
=
fileName
;
sceneFileName
=
fileName
;
...
...
public/index.html
View file @
af3bbe7d
...
@@ -9,15 +9,8 @@
...
@@ -9,15 +9,8 @@
<link
rel=
"stylesheet"
href=
"style.css"
>
<link
rel=
"stylesheet"
href=
"style.css"
>
<!-- Three.js (modern ES module setup) -->
<script
src=
"https://unpkg.com/three@0.126.0/build/three.js"
></script>
<script
type=
"importmap"
>
<script
src=
"https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"
></script>
{
"
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>
</head>
<body>
<body>
...
@@ -306,7 +299,7 @@
...
@@ -306,7 +299,7 @@
============================================================ -->
============================================================ -->
<div
id=
"app-version"
>
<div
id=
"app-version"
>
v0.1.
4
v0.1.
3
</div>
</div>
...
@@ -314,14 +307,11 @@
...
@@ -314,14 +307,11 @@
JAVASCRIPT
JAVASCRIPT
============================================================ -->
============================================================ -->
<!-- Debug console loads first so it can capture module errors/logs -->
<script
src=
"ar_start.js"
></script>
<script
src=
"ar_debug_console.js"
></script>
<script
src=
"ar_main.js"
></script>
<!-- Application modules -->
<script
src=
"ar_debug_console.js"
></script>
<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
<link
rel=
"stylesheet"
rel=
"stylesheet"
...
@@ -329,7 +319,9 @@
...
@@ -329,7 +319,9 @@
<script
src=
"https://unpkg.com/leaflet/dist/leaflet.js"
></script>
<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