Skip to content

Instantly share code, notes, and snippets.

@danvk
Last active April 14, 2020 14:32
Show Gist options
  • Save danvk/a1864cf1422ebc7958c8c76a2395db37 to your computer and use it in GitHub Desktop.
Save danvk/a1864cf1422ebc7958c8c76a2395db37 to your computer and use it in GitHub Desktop.
Repro of texture issue for MapboxGL and THREE.js
<!doctype html>
<body></body>
<script type="module" src="statue-three.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Update a choropleth layer by zoom level</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.4.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.4.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = '<insert your token here>';
</script>
<script src="token.js"></script>
<script src="statue-mapbox.js" type="module"></script>
</body>
</html>
import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js';
import * as dat from 'https://unpkg.com/[email protected]/build/dat.gui.module.js';
const {MercatorCoordinate} = mapboxgl;
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v9',
center: [-74.0445, 40.6892],
zoom: 16,
pitch: 60,
bearing: 120,
});
class DimensionGUIHelper {
constructor(obj, minProp, maxProp) {
this.obj = obj;
this.minProp = minProp;
this.maxProp = maxProp;
}
get value() {
return this.obj[this.maxProp] * 2;
}
set value(v) {
this.obj[this.maxProp] = v / 2;
this.obj[this.minProp] = v / -2;
}
}
class SpriteCustomLayer {
type = 'custom';
renderingMode = '3d';
constructor(id) {
this.id = id;
this.gui = new dat.GUI();
THREE.Object3D.DefaultUp.set(0, 0, 1);
}
async onAdd(map, gl) {
this.camera = new THREE.Camera();
const centerLngLat = map.getCenter();
this.center = MercatorCoordinate.fromLngLat(centerLngLat, 0);
const {x, y, z} = this.center;
this.cameraTransform = new THREE.Matrix4()
.makeTranslation(x, y, z)
.scale(new THREE.Vector3(1, -1, 1));
this.map = map;
this.scene = this.makeScene();
// use the Mapbox GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
});
this.renderer.shadowMap.enabled = true;
this.renderer.autoClear = false;
}
makeScene() {
const scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0xffffff, 0.25));
const s = this.center.meterInMercatorCoordinateUnits();
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0.000002360847837325531, 0.000004566603480958114, 0.00000725142167844218);
light.target.position.set(0, 0, 0);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
// light.shadow.radius *= s;
light.shadow.camera.left = -0.000002383416166278454 * 2;
light.shadow.camera.right = 0.000002383416166278454 * 2;
light.shadow.camera.bottom = -0.000002383416166278454 * 2;
light.shadow.camera.top = 0.000002383416166278454 * 2;
light.shadow.camera.near = 0.0000012388642793465356;
light.shadow.camera.far *= s;
console.log(light.shadow.camera.rotation.order);
// light.shadow.camera.zoom = s;
scene.add(light);
this.light = light;
const cameraHelper = new THREE.CameraHelper(light.shadow.camera);
scene.add(cameraHelper);
const lightHelper = new THREE.DirectionalLightHelper(light, 10 * s);
scene.add(lightHelper);
const update = () => {
light.target.updateMatrixWorld();
lightHelper.update();
light.shadow.camera.updateProjectionMatrix();
cameraHelper.update();
};
let folder = this.gui.addFolder('light');
folder.add(light.position, 'x', -1e-5, 1e-5).onChange(update);
folder.add(light.position, 'y', -1e-5, 1e-5).onChange(update);
folder.add(light.position, 'z', -1e-5, 1e-5).onChange(update);
folder.open();
folder = this.gui.addFolder('shadow camera');
folder
.add(new DimensionGUIHelper(light.shadow.camera, 'left', 'right'), 'value', s, 200 * s)
.name('width')
.onChange(update);
folder
.add(new DimensionGUIHelper(light.shadow.camera, 'bottom', 'top'), 'value', s, 200 * s)
.name('height')
.onChange(update);
folder.add(light.shadow.camera, 'zoom', 0.01, 1.5, 0.01).onChange(update);
folder.add(light.shadow.camera, 'near', 0, 1000 * s).onChange(update);
folder.add(light.shadow.camera, 'far', 0, 1000 * s).onChange(update);
folder.open();
{
const planeSize = 500;
const loader = new THREE.TextureLoader();
const texture = loader.load('/checker.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.NearestFilter;
const repeats = 10;
texture.repeat.set(repeats, repeats);
const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeo, planeMat);
plane.scale.setScalar(s);
plane.rotation.x = Math.PI;
plane.receiveShadow = true;
scene.add(plane);
}
{
const sphereRadius = 5e-7;
const sphereGeo = new THREE.SphereBufferGeometry(sphereRadius, 32, 32);
const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
const mesh = new THREE.Mesh(sphereGeo, sphereMat);
mesh.position.set(0, 0, 5e-6);
mesh.castShadow = true;
mesh.receiveShadow = false;
sphereMat.side = THREE.DoubleSide;
scene.add(mesh);
const folder = this.gui.addFolder('sphere');
folder.add(mesh.position, 'x', -1e-5, 1e-5);
folder.add(mesh.position, 'y', -1e-5, 1e-5);
folder.add(mesh.position, 'z', -1e-5, 1e-5);
folder.open();
}
return scene;
}
render(gl, matrix) {
this.camera.projectionMatrix = new THREE.Matrix4()
.fromArray(matrix)
.multiply(this.cameraTransform);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
}
map.on('load', () => {
const layer = new SpriteCustomLayer('statue');
map.addLayer(layer);
});
import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js';
import {OrbitControls} from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';
import {MTLLoader} from 'https://unpkg.com/[email protected]/examples/jsm/loaders/MTLLoader.js';
import {MtlObjBridge} from 'https://unpkg.com/[email protected]/examples/jsm/loaders/obj2/bridge/MtlObjBridge.js';
import {OBJLoader2} from 'https://unpkg.com/[email protected]/examples/jsm/loaders/OBJLoader2.js';
var camera, scene, renderer;
init();
animate();
async function addOBJ(path, mtlPath) {
const loader = new OBJLoader2();
const matLoader = new MTLLoader();
matLoader.load(mtlPath, mtlParseResult => {
const materials = MtlObjBridge.addMaterialsFromMtlLoader(mtlParseResult);
loader.addMaterials(materials);
loader.load(path, group => {
scene.add(group);
});
});
}
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.001, 1000 );
camera.position.set(86, 100, -5);
scene = new THREE.Scene();
scene.add(new THREE.AmbientLight( 0xffffff, 0.25 ));
for (const y of [-70, 70]) {
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, y, 100).normalize();
scene.add(directionalLight);
}
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();
addOBJ('silo_p2.obj', 'silo_p2.mtl');
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
<!doctype html>
<body></body>
<script type="module" src="statue-three.js"></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment