Skip to content

Instantly share code, notes, and snippets.

@darrenwiens
Created March 13, 2022 22:10
Show Gist options
  • Save darrenwiens/de5fd76a165d22b78eaf8073efa6972b to your computer and use it in GitHub Desktop.
Save darrenwiens/de5fd76a165d22b78eaf8073efa6972b to your computer and use it in GitHub Desktop.
GLTF playground, Mapbox
<!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