Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
GeoVistoogsi
AR
Commits
d9eb7113
Commit
d9eb7113
authored
4 months ago
by
Percen
Browse files
Options
Download
Email Patches
Plain Diff
Update public/index.html
parent
d5ba084b
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
public/index.html
+232
-259
public/index.html
with
232 additions
and
259 deletions
+232
-259
public/index.html
+
232
-
259
View file @
d9eb7113
<!
DOCTYPE
html>
<!
doctype
html>
<html
lang=
"de"
>
<html
lang=
"de"
>
<head>
<head>
<meta
charset=
"UTF-8"
>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
3D Modell Umschalter - Sonnenblume und Roboter
</title>
<title>
GeoVis AR Projekt
</title>
<style>
<style>
body
{
body
{
margin
:
0
;
margin
:
0
;
overflow
:
hidden
;
font-family
:
Arial
,
sans-serif
;
background-color
:
#e0e0e0
;
background-color
:
#f0f0f0
;
}
color
:
#333
;
canvas
{
display
:
flex
;
display
:
block
;
justify-content
:
center
;
}
align-items
:
center
;
</style>
height
:
100vh
;
background-image
:
url('https://www.hft-stuttgart.de/fileadmin/Dateien/Hochschule/-_R_Juergen_Pollak_HFT_18.04.18-0091.jpg')
;
background-size
:
cover
;
background-position
:
center
;
}
.container
{
text-align
:
center
;
background-color
:
rgba
(
0
,
0
,
0
,
0.5
);
padding
:
50px
;
border-radius
:
10px
;
color
:
white
;
max-width
:
80%
;
box-shadow
:
0px
0px
15px
rgba
(
0
,
0
,
0
,
0.5
);
}
//
teste
h1
{
font-size
:
3em
;
margin-bottom
:
20px
;
}
p
{
font-size
:
1.2em
;
margin-bottom
:
30px
;
}
button
{
background-color
:
#4CAF50
;
color
:
white
;
font-size
:
1.5em
;
padding
:
15px
30px
;
border
:
none
;
border-radius
:
5px
;
cursor
:
pointer
;
transition
:
background-color
0.3s
;
margin
:
10px
;
}
button
:hover
{
background-color
:
#45a049
;
}
button
:active
{
background-color
:
#387a39
;
}
</style>
<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>
</head>
</head>
<body>
<body>
<script
src=
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"
></script>
<script>
<script
src=
"https://threejs.org/examples/js/controls/OrbitControls.js"
></script>
let
selectedModel
=
'
car
'
;
// Standardauswahl
<script
src=
"https://threejs.org/examples/js/loaders/GLTFLoader.js"
></script>
let
models
=
{};
<script
src=
"https://threejs.org/examples/js/webxr/VRButton.js"
></script>
let
reticle
;
<script
src=
"https://cdn.jsdelivr.net/npm/three-mesh-ui@1.5.0/dist/three-mesh-ui.js"
></script>
let
menu
;
<script>
async
function
activateXR
()
{
import
*
as
THREE
from
'
https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.module.js
'
;
const
canvas
=
document
.
createElement
(
'
canvas
'
);
import
{
VRButton
}
from
'
https://threejs.org/examples/jsm/webxr/VRButton.js
'
;
document
.
body
.
appendChild
(
canvas
);
import
{
OrbitControls
}
from
'
https://threejs.org/examples/jsm/controls/OrbitControls.js
'
;
const
gl
=
canvas
.
getContext
(
'
webgl
'
,
{
xrCompatible
:
true
});
import
{
GLTFLoader
}
from
'
https://threejs.org/examples/jsm/loaders/GLTFLoader.js
'
;
const
renderer
=
new
THREE
.
WebGLRenderer
({
alpha
:
true
,
canvas
,
context
:
gl
});
import
ThreeMeshUI
from
'
https://cdn.jsdelivr.net/npm/three-mesh-ui@1.5.0/dist/three-mesh-ui.js
'
;
renderer
.
autoClear
=
false
;
let
scene
,
camera
,
renderer
,
controls
;
const
scene
=
new
THREE
.
Scene
();
let
sunflowerModel
,
robotModel
;
const
camera
=
new
THREE
.
PerspectiveCamera
();
let
currentModel
=
'
sunflower
'
;
// Default is sunflower
camera
.
matrixAutoUpdate
=
false
;
window
.
addEventListener
(
'
load
'
,
init
);
// Licht hinzufügen
window
.
addEventListener
(
'
resize
'
,
onWindowResize
);
const
light
=
new
THREE
.
DirectionalLight
(
0xffffff
,
1
);
light
.
position
.
set
(
10
,
10
,
10
);
const
objsToTest
=
[];
scene
.
add
(
light
);
const
raycaster
=
new
THREE
.
Raycaster
();
const
mouse
=
new
THREE
.
Vector2
();
// Reticle (Cursor)
let
selectState
=
false
;
const
loader
=
new
THREE
.
GLTFLoader
();
loader
.
load
(
"
https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf
"
,
(
gltf
)
=>
{
window
.
addEventListener
(
'
pointermove
'
,
(
event
)
=>
{
reticle
=
gltf
.
scene
;
mouse
.
x
=
(
event
.
clientX
/
window
.
innerWidth
)
*
2
-
1
;
reticle
.
visible
=
false
;
mouse
.
y
=
-
(
event
.
clientY
/
window
.
innerHeight
)
*
2
+
1
;
scene
.
add
(
reticle
);
});
});
window
.
addEventListener
(
'
pointerdown
'
,
()
=>
{
const
container
=
new
ThreeMeshUI
.
Block
({
selectState
=
true
;
width
:
1.2
,
});
height
:
0.7
,
padding
:
0.2
,
window
.
addEventListener
(
'
pointerup
'
,
()
=>
{
fontFamily
:
'
./assets/Roboto-msdf.json
'
,
selectState
=
false
;
fontTexture
:
'
./assets/Roboto-msdf.png
'
,
});
});
window
.
addEventListener
(
'
touchstart
'
,
(
event
)
=>
{
//
selectState
=
true
;
mouse
.
x
=
(
event
.
touches
[
0
].
clientX
/
window
.
innerWidth
)
*
2
-
1
;
const
text
=
new
ThreeMeshUI
.
Text
({
mouse
.
y
=
-
(
event
.
touches
[
0
].
clientY
/
window
.
innerHeight
)
*
2
+
1
;
content
:
"
Some text to be displayed
"
});
});
window
.
addEventListener
(
'
touchend
'
,
()
=>
{
container
.
add
(
text
);
selectState
=
false
;
mouse
.
x
=
null
;
// scene is a THREE.Scene (see three.js)
mouse
.
y
=
null
;
scene
.
add
(
container
);
});
// This is typically done in the render loop :
function
init
()
{
ThreeMeshUI
.
update
();
scene
=
new
THREE
.
Scene
();
// Modelle laden
scene
.
background
=
new
THREE
.
Color
(
0x505050
);
loader
.
load
(
"
https://threejs.org/examples/models/gltf/RobotExpressive/RobotExpressive.glb
"
,
(
gltf
)
=>
{
models
.
car
=
gltf
.
scene
;
camera
=
new
THREE
.
PerspectiveCamera
(
75
,
window
.
innerWidth
/
window
.
innerHeight
,
0.1
,
1000
);
});
loader
.
load
(
"
https://immersive-web.github.io/webxr-samples/media/gltf/sunflower/sunflower.gltf
"
,
(
gltf
)
=>
{
renderer
=
new
THREE
.
WebGLRenderer
({
antialias
:
true
});
models
.
lamp
=
gltf
.
scene
;
renderer
.
setPixelRatio
(
window
.
devicePixelRatio
);
});
renderer
.
setSize
(
window
.
innerWidth
,
window
.
innerHeight
);
renderer
.
outputEncoding
=
THREE
.
sRGBEncoding
;
// 3D-Menü erstellen
renderer
.
xr
.
enabled
=
true
;
const
menuGeometry
=
new
THREE
.
PlaneGeometry
(
0.5
,
0.2
);
// Menügröße
document
.
body
.
appendChild
(
VRButton
.
createButton
(
renderer
));
const
menuMaterial
=
new
THREE
.
MeshBasicMaterial
({
color
:
0x333333
,
opacity
:
0.8
,
transparent
:
true
});
document
.
body
.
appendChild
(
renderer
.
domElement
);
menu
=
new
THREE
.
Mesh
(
menuGeometry
,
menuMaterial
);
menu
.
position
.
set
(
0
,
-
0.5
,
-
1
);
// Unten im Kamerasichtfeld
controls
=
new
OrbitControls
(
camera
,
renderer
.
domElement
);
scene
.
add
(
menu
);
camera
.
position
.
set
(
0
,
1.6
,
0
);
controls
.
target
=
new
THREE
.
Vector3
(
0
,
1
,
-
1.8
);
// Menü-Buttons als Flächen
const
buttonGeometry
=
new
THREE
.
PlaneGeometry
(
0.15
,
0.1
);
const
room
=
new
THREE
.
LineSegments
(
const
buttonMaterial1
=
new
THREE
.
MeshBasicMaterial
({
color
:
0x2196F3
});
new
THREE
.
BoxGeometry
(
6
,
6
,
6
,
10
,
10
,
10
).
translate
(
0
,
3
,
0
),
const
buttonMaterial2
=
new
THREE
.
MeshBasicMaterial
({
color
:
0xFF9800
});
new
THREE
.
LineBasicMaterial
({
color
:
0x808080
})
);
const
carButton
=
new
THREE
.
Mesh
(
buttonGeometry
,
buttonMaterial1
);
carButton
.
position
.
set
(
-
0.2
,
-
0.5
,
-
0.99
);
// Links unten
const
roomMesh
=
new
THREE
.
Mesh
(
scene
.
add
(
carButton
);
new
THREE
.
BoxGeometry
(
6
,
6
,
6
,
10
,
10
,
10
).
translate
(
0
,
3
,
0
),
new
THREE
.
MeshBasicMaterial
({
side
:
THREE
.
BackSide
})
const
lampButton
=
new
THREE
.
Mesh
(
buttonGeometry
,
buttonMaterial2
);
);
lampButton
.
position
.
set
(
0.2
,
-
0.5
,
-
0.99
);
// Rechts unten
scene
.
add
(
lampButton
);
scene
.
add
(
room
);
objsToTest
.
push
(
roomMesh
);
const
light
=
new
THREE
.
DirectionalLight
(
0xffffff
,
1
);
// Raycaster für Button-Interaktion
light
.
position
.
set
(
5
,
5
,
5
).
normalize
();
const
raycaster
=
new
THREE
.
Raycaster
();
scene
.
add
(
light
);
const
pointer
=
new
THREE
.
Vector2
();
const
hemLight
=
new
THREE
.
HemisphereLight
(
0x808080
,
0x606060
);
// AR-Session starten
scene
.
add
(
hemLight
);
const
session
=
await
navigator
.
xr
.
requestSession
(
'
immersive-ar
'
,
{
requiredFeatures
:
[
'
hit-test
'
]
});
session
.
updateRenderState
({
baseLayer
:
new
XRWebGLLayer
(
session
,
gl
)
});
// Models
const
referenceSpace
=
await
session
.
requestReferenceSpace
(
'
local
'
);
const
loader
=
new
GLTFLoader
();
const
viewerSpace
=
await
session
.
requestReferenceSpace
(
'
viewer
'
);
loader
.
load
(
'
https://immersive-web.github.io/webxr-samples/media/gltf/sunflower/sunflower.gltf
'
,
(
gltf
)
=>
{
const
hitTestSource
=
await
session
.
requestHitTestSource
({
space
:
viewerSpace
});
sunflowerModel
=
gltf
.
scene
;
sunflowerModel
.
scale
.
set
(
0.5
,
0.5
,
0.5
);
session
.
requestAnimationFrame
(
onXRFrame
);
sunflowerModel
.
position
.
set
(
0
,
0
,
-
2
);
scene
.
add
(
sunflowerModel
);
session
.
addEventListener
(
"
select
"
,
(
event
)
=>
{
});
if
(
!
reticle
)
return
;
loader
.
load
(
'
https://threejs.org/examples/models/gltf/RobotExpressive/RobotExpressive.glb
'
,
(
gltf
)
=>
{
robotModel
=
gltf
.
scene
;
// Raycaster von der Kameraposition (Mitte des Bildschirms)
robotModel
.
scale
.
set
(
0.5
,
0.5
,
0.5
);
raycaster
.
setFromCamera
(
pointer
,
camera
);
robotModel
.
position
.
set
(
0
,
0
,
-
2
);
const
intersects
=
raycaster
.
intersectObjects
([
carButton
,
lampButton
]);
scene
.
add
(
robotModel
);
});
// Prüfen, ob ein Button getroffen wurde
if
(
intersects
.
length
>
0
)
{
// UI Panel
const
clickedButton
=
intersects
[
0
].
object
;
makePanel
();
if
(
clickedButton
===
carButton
)
{
// Start animation loop
selectedModel
=
'
car
'
;
renderer
.
setAnimationLoop
(
loop
);
console
.
log
(
'
Auto ausgewählt
'
);
}
}
else
if
(
clickedButton
===
lampButton
)
{
selectedModel
=
'
lamp
'
;
function
makePanel
()
{
console
.
log
(
'
Laterne ausgewählt
'
);
const
container
=
new
ThreeMeshUI
.
Block
({
}
justifyContent
:
'
center
'
,
}
contentDirection
:
'
row-reverse
'
,
// Kein Button getroffen -> Modell platzieren
fontFamily
:
'
./Roboto-msdf.json
'
,
else
if
(
models
[
selectedModel
])
{
fontTexture
:
'
./Roboto-msdf.png
'
,
const
clone
=
models
[
selectedModel
].
clone
();
fontSize
:
0.07
,
clone
.
position
.
copy
(
reticle
.
position
);
padding
:
0.02
,
clone
.
scale
.
set
(
0.5
,
0.5
,
0.5
);
// Größe anpassen
borderRadius
:
0.11
scene
.
add
(
clone
);
});
console
.
log
(
`
${
selectedModel
}
platziert`
);
container
.
position
.
set
(
0
,
0.6
,
-
1.2
);
container
.
rotation
.
x
=
-
0.55
;
scene
.
add
(
container
);
const
buttonOptions
=
{
width
:
0.4
,
height
:
0.15
,
justifyContent
:
'
center
'
,
offset
:
0.05
,
margin
:
0.02
,
borderRadius
:
0.075
};
const
hoveredStateAttributes
=
{
state
:
'
hovered
'
,
attributes
:
{
offset
:
0.035
,
backgroundColor
:
new
THREE
.
Color
(
0x999999
),
backgroundOpacity
:
1
,
fontColor
:
new
THREE
.
Color
(
0xffffff
)
}
};
const
idleStateAttributes
=
{
state
:
'
idle
'
,
attributes
:
{
offset
:
0.035
,
backgroundColor
:
new
THREE
.
Color
(
0x666666
),
backgroundOpacity
:
0.3
,
fontColor
:
new
THREE
.
Color
(
0xffffff
)
}
};
const
buttonSunflower
=
new
ThreeMeshUI
.
Block
(
buttonOptions
);
const
buttonRobot
=
new
ThreeMeshUI
.
Block
(
buttonOptions
);
buttonSunflower
.
add
(
new
ThreeMeshUI
.
Text
({
content
:
'
Sonnenblume
'
}));
buttonRobot
.
add
(
new
ThreeMeshUI
.
Text
({
content
:
'
Roboter
'
}));
const
selectedAttributes
=
{
offset
:
0.02
,
backgroundColor
:
new
THREE
.
Color
(
0x777777
),
fontColor
:
new
THREE
.
Color
(
0x222222
)
};
buttonSunflower
.
setupState
({
state
:
'
selected
'
,
attributes
:
selectedAttributes
,
onSet
:
()
=>
{
currentModel
=
'
sunflower
'
;
showModel
();
}
});
buttonSunflower
.
setupState
(
hoveredStateAttributes
);
buttonSunflower
.
setupState
(
idleStateAttributes
);
buttonRobot
.
setupState
({
state
:
'
selected
'
,
attributes
:
selectedAttributes
,
onSet
:
()
=>
{
currentModel
=
'
robot
'
;
showModel
();
}
});
buttonRobot
.
setupState
(
hoveredStateAttributes
);
buttonRobot
.
setupState
(
idleStateAttributes
);
container
.
add
(
buttonSunflower
,
buttonRobot
);
objsToTest
.
push
(
buttonSunflower
,
buttonRobot
);
}
function
showModel
()
{
if
(
sunflowerModel
)
sunflowerModel
.
visible
=
currentModel
===
'
sunflower
'
;
if
(
robotModel
)
robotModel
.
visible
=
currentModel
===
'
robot
'
;
}
}
});
function
onWindowResize
()
{
camera
.
aspect
=
window
.
innerWidth
/
window
.
innerHeight
;
// AR-Rendering
camera
.
updateProjectionMatrix
();
function
onXRFrame
(
time
,
frame
)
{
renderer
.
setSize
(
window
.
innerWidth
,
window
.
innerHeight
);
session
.
requestAnimationFrame
(
onXRFrame
);
}
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
session
.
renderState
.
baseLayer
.
framebuffer
);
function
loop
()
{
const
pose
=
frame
.
getViewerPose
(
referenceSpace
);
ThreeMeshUI
.
update
();
if
(
pose
)
{
controls
.
update
();
const
view
=
pose
.
views
[
0
];
renderer
.
render
(
scene
,
camera
);
const
viewport
=
session
.
renderState
.
baseLayer
.
getViewport
(
view
);
updateButtons
();
renderer
.
setSize
(
viewport
.
width
,
viewport
.
height
);
}
camera
.
matrix
.
fromArray
(
view
.
transform
.
matrix
);
function
updateButtons
()
{
camera
.
projectionMatrix
.
fromArray
(
view
.
projectionMatrix
);
let
intersect
;
camera
.
updateMatrixWorld
(
true
);
if
(
mouse
.
x
!==
null
&&
mouse
.
y
!==
null
)
{
// Menü bleibt fixiert vor der Kamera
raycaster
.
setFromCamera
(
mouse
,
camera
);
menu
.
position
.
set
(
0
,
-
0.5
,
-
1
);
intersect
=
raycast
();
menu
.
lookAt
(
camera
.
position
);
}
carButton
.
position
.
set
(
-
0.2
,
-
0.5
,
-
0.99
);
if
(
intersect
&&
intersect
.
object
.
isUI
)
{
lampButton
.
position
.
set
(
0.2
,
-
0.5
,
-
0.99
);
if
(
selectState
)
{
intersect
.
object
.
setState
(
'
selected
'
);
const
hitTestResults
=
frame
.
getHitTestResults
(
hitTestSource
);
}
else
{
if
(
hitTestResults
.
length
>
0
)
{
intersect
.
object
.
setState
(
'
hovered
'
);
const
hitPose
=
hitTestResults
[
0
].
getPose
(
referenceSpace
);
}
reticle
.
visible
=
true
;
}
reticle
.
position
.
set
(
hitPose
.
transform
.
position
.
x
,
hitPose
.
transform
.
position
.
y
,
hitPose
.
transform
.
position
.
z
);
reticle
.
updateMatrixWorld
(
true
);
objsToTest
.
forEach
((
obj
)
=>
{
}
if
((
!
intersect
||
obj
!==
intersect
.
object
)
&&
obj
.
isUI
)
{
obj
.
setState
(
'
idle
'
);
renderer
.
render
(
scene
,
camera
);
}
});
}
function
raycast
()
{
return
objsToTest
.
reduce
((
closestIntersection
,
obj
)
=>
{
const
intersection
=
raycaster
.
intersectObject
(
obj
,
true
);
if
(
!
intersection
[
0
])
return
closestIntersection
;
if
(
!
closestIntersection
||
intersection
[
0
].
distance
<
closestIntersection
.
distance
)
{
intersection
[
0
].
object
=
obj
;
return
intersection
[
0
];
}
return
closestIntersection
;
},
null
);
}
}
</script>
}
}
// AR starten
if
(
navigator
.
xr
)
{
const
startButton
=
document
.
createElement
(
'
button
'
);
startButton
.
textContent
=
'
Start AR
'
;
startButton
.
style
.
cssText
=
"
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 15px; font-size: 18px;
"
;
document
.
body
.
appendChild
(
startButton
);
startButton
.
onclick
=
()
=>
{
startButton
.
remove
();
activateXR
();
};
}
else
{
alert
(
'
WebXR wird nicht unterstützt.
'
);
}
</script>
</body>
</body>
</html>
</html>
This diff is collapsed.
Click to expand it.
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