Created
March 13, 2022 22:10
-
-
Save darrenwiens/de5fd76a165d22b78eaf8073efa6972b to your computer and use it in GitHub Desktop.
GLTF playground, Mapbox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>glTF Playground</title> | |
<meta | |
name="viewport" | |
content="initial-scale=1,maximum-scale=1,user-scalable=no" | |
/> | |
<script src="https://api.mapbox.com/mapbox-gl-js/v2.3.0/mapbox-gl.js"></script> | |
<link | |
href="https://api.mapbox.com/mapbox-gl-js/v2.3.0/mapbox-gl.css" | |
rel="stylesheet" | |
/> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
} | |
#map { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
width: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="https://unpkg.com/three@0.106.2/build/three.min.js"></script> | |
<script src="https://unpkg.com/three@0.106.2/examples/js/loaders/GLTFLoader.js"></script> | |
<script src="https://unpkg.com/three@0.106.2/examples/js/loaders/DRACOLoader.js"></script> | |
<script | |
type="text/javascript" | |
src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js" | |
></script> | |
<div id="map"></div> | |
<script> | |
prevTime = Date.now(); | |
var mixer; | |
mapboxgl.accessToken = | |
"<YOUR_MAPBOX_API_KEY>"; | |
var map = (window.map = new mapboxgl.Map({ | |
container: "map", | |
style: "mapbox://styles/mapbox/satellite-v9", | |
zoom: 13.94, | |
center: [-122.48114, 37.81845], | |
pitch: 68, | |
bearing: 43.2, | |
antialias: true, // create the gl context with MSAA antialiasing, so custom layers are antialiased | |
})); | |
var guiObject = function () { | |
this.model = | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Stork.glb"; | |
this.dx = 0; | |
this.dy = 0; | |
this.dz = 0; | |
this.rotation_x = 1; | |
this.rotation_y = 0; | |
this.rotation_z = 0; | |
this.scale = 0.25; | |
this.Source = "."; | |
this.Contact = "."; | |
}; | |
var guiInstance = new guiObject(); | |
models = {}; | |
map.on("load", async function () { | |
var gui = new dat.GUI(); | |
function get_models() { | |
var options = { | |
Head: "https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/LeePerrySmith/LeePerrySmith.glb", | |
Duck: "https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Duck/glTF/Duck.gltf", | |
MetalRoughSpheres: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/MetalRoughSpheres/glTF/MetalRoughSpheres.gltf", | |
Monster: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Monster/glTF/Monster.gltf", | |
Nefertiti: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Nefertiti/Nefertiti.glb", | |
Flamingo: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Flamingo.glb", | |
Horse: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Horse.glb", | |
Parrot: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Parrot.glb", | |
Stork: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/Stork.glb", | |
Robot: | |
"https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/RobotExpressive/RobotExpressive.glb", | |
}; | |
var params = gui.addFolder("Model"); | |
params.add(guiInstance, "model", options).onChange(function (value) { | |
map.removeLayer("3d-model"); | |
map.addLayer(customLayer); | |
}); | |
return options; | |
} | |
var models = await get_models(); | |
var params = gui.addFolder("Movement"); | |
params.add(guiInstance, "dx", -1, 1, 0.0001); | |
params.add(guiInstance, "dy", -1, 1, 0.0001); | |
params.add(guiInstance, "dz", -1, 1, 0.0001); | |
var params = gui.addFolder("Rotation"); | |
params.add(guiInstance, "rotation_x", -2, 2, 0.0001); | |
params.add(guiInstance, "rotation_y", -2, 2, 0.0001); | |
params.add(guiInstance, "rotation_z", -2, 2, 0.0001); | |
var params = gui.addFolder("Scale"); | |
params.add(guiInstance, "scale", 0, 10, 0.0001); | |
var params = gui.addFolder("Data"); | |
params.add(guiInstance, "Source"); | |
params.add(guiInstance, "Source"); | |
var params = gui.addFolder("Contact"); | |
params.add(guiInstance, "Contact"); | |
gui.__folders["Data"].__controllers[0].domElement.innerHTML = | |
"<a href='https://github.com/mrdoob/three.js/tree/master/examples/models/gltf' target='_blank'>Three.js examples</a>"; | |
gui.__folders["Contact"].__controllers[0].domElement.innerHTML = | |
"Made by <a href='https://twitter.com/dkwiens' target='_blank'>@dkwiens</a>"; | |
map.setFog({ | |
range: [-1, 1.5], | |
color: "white", | |
"horizon-blend": 0.1, | |
}); | |
map.addSource("mapbox-dem", { | |
type: "raster-dem", | |
url: "mapbox://mapbox.mapbox-terrain-dem-v1", | |
tileSize: 512, | |
maxzoom: 14, | |
}); | |
// add the DEM source as a terrain layer with exaggerated height | |
map.setTerrain({ source: "mapbox-dem", exaggeration: 1.6 }); | |
// add a sky layer that will show when the map is highly pitched | |
map.addLayer({ | |
id: "sky", | |
type: "sky", | |
paint: { | |
"sky-type": "atmosphere", | |
"sky-atmosphere-sun": [0.0, 0.0], | |
"sky-atmosphere-sun-intensity": 15, | |
}, | |
}); | |
}); | |
var requestOptions = { | |
method: "GET", | |
redirect: "follow", | |
}; | |
// parameters to ensure the model is georeferenced correctly on the map | |
var modelOrigin = [-122.478968, 37.822742]; | |
var modelAltitude = 300; | |
var modelRotate = [Math.PI / 2, 0, 0]; | |
var modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat( | |
modelOrigin, | |
modelAltitude | |
); | |
var scaleScale = 1000000; | |
// transformation parameters to position, rotate and scale the 3D model onto the map | |
var modelTransform = { | |
translateX: modelAsMercatorCoordinate.x, | |
translateY: modelAsMercatorCoordinate.y, | |
translateZ: modelAsMercatorCoordinate.z, | |
rotateX: modelRotate[0], | |
rotateY: modelRotate[1], | |
rotateZ: modelRotate[2], | |
scale: guiInstance.scale / scaleScale, | |
}; | |
var THREE = window.THREE; | |
// configuration of the custom layer for a 3D model per the CustomLayerInterface | |
var customLayer = { | |
id: "3d-model", | |
type: "custom", | |
renderingMode: "3d", | |
onAdd: function (map, gl) { | |
this.camera = new THREE.Camera(); | |
this.scene = new THREE.Scene(); | |
// create two three.js lights to illuminate the model | |
var directionalLight = new THREE.DirectionalLight(0xffffff); | |
directionalLight.position.set(0, -70, 100).normalize(); | |
this.scene.add(directionalLight); | |
var directionalLight2 = new THREE.DirectionalLight(0xffffff); | |
directionalLight2.position.set(0, 70, 100).normalize(); | |
this.scene.add(directionalLight2); | |
// use the three.js GLTF loader to add the 3D model to the three.js scene | |
var loader = new THREE.GLTFLoader(); | |
var dracoloader = new THREE.DRACOLoader(); | |
THREE.DRACOLoader.setDecoderPath( | |
"https://www.gstatic.com/draco/v1/decoders/" | |
); | |
loader.setDRACOLoader(dracoloader); | |
var model = guiInstance.model; | |
loader.load( | |
model, | |
function (gltf) { | |
this.scene.add(gltf.scene); | |
mixer = new THREE.AnimationMixer(gltf.scene); | |
mixer.clipAction(gltf.animations[0]).setDuration(1).play(); | |
}.bind(this) | |
); | |
this.map = map; | |
// use the Mapbox GL JS map canvas for three.js | |
this.renderer = new THREE.WebGLRenderer({ | |
canvas: map.getCanvas(), | |
context: gl, | |
antialias: true, | |
}); | |
this.renderer.autoClear = false; | |
}, | |
render: function (gl, matrix) { | |
var rotationX = new THREE.Matrix4().makeRotationAxis( | |
new THREE.Vector3(1, 0, 0), | |
// modelTransform.rotateX | |
(Math.PI / 2) * guiInstance.rotation_x | |
); | |
var rotationY = new THREE.Matrix4().makeRotationAxis( | |
new THREE.Vector3(0, 1, 0), | |
(Math.PI / 2) * guiInstance.rotation_y | |
); | |
var rotationZ = new THREE.Matrix4().makeRotationAxis( | |
new THREE.Vector3(0, 0, 1), | |
(Math.PI / 2) * guiInstance.rotation_z | |
); | |
var m = new THREE.Matrix4().fromArray(matrix); | |
modelAltitude = modelAltitude + guiInstance.dz; | |
modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat( | |
modelOrigin, | |
modelAltitude | |
); | |
modelTransform.translateZ = modelAsMercatorCoordinate.z; | |
modelOrigin = [ | |
modelOrigin[0] + guiInstance.dx / 1000, | |
modelOrigin[1] + guiInstance.dy / 1000, | |
]; | |
modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat( | |
modelOrigin, | |
modelAltitude | |
); | |
modelTransform.translateX = modelAsMercatorCoordinate.x; | |
modelTransform.translateY = modelAsMercatorCoordinate.y; | |
modelTransform.scale = guiInstance.scale / scaleScale; | |
var l = new THREE.Matrix4() | |
.makeTranslation( | |
modelTransform.translateX, | |
modelTransform.translateY, | |
modelTransform.translateZ | |
) | |
.scale( | |
new THREE.Vector3( | |
modelTransform.scale, | |
-modelTransform.scale, | |
modelTransform.scale | |
) | |
) | |
.multiply(rotationX) | |
.multiply(rotationY) | |
.multiply(rotationZ); | |
if (mixer) { | |
const time = Date.now(); | |
mixer.update((time - prevTime) * 0.001); | |
prevTime = time; | |
} | |
this.camera.projectionMatrix = m.multiply(l); | |
this.renderer.state.reset(); | |
this.renderer.render(this.scene, this.camera); | |
this.map.triggerRepaint(); | |
}, | |
}; | |
map.on("style.load", function () { | |
map.addLayer(customLayer); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment