Created
February 25, 2020 23:27
-
-
Save danvk/1c76f51822873b5a38b1bef2e355b6cd to your computer and use it in GitHub Desktop.
Flailing around with raycasting
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>Raycasting with Mapbox</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 src="raycast.js" type="module"></script> | |
</body> | |
</html> |
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
import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js'; | |
import * as mat4 from 'https://unpkg.com/[email protected]/esm/mat4.js'; | |
const {MercatorCoordinate} = mapboxgl; | |
mapboxgl.accessToken = 'pk.eyJ1IjoiZGFudmsiLCJhIjoiY2lrZzJvNDR0MDBhNXR4a2xqNnlsbWx3ciJ9.myJhweYd_hrXClbKk8XLgQ'; | |
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 BoxCustomLayer { | |
type = 'custom'; | |
renderingMode = '3d'; | |
constructor(id) { | |
this.id = id; | |
THREE.Object3D.DefaultUp.set(0, 0, 1); | |
} | |
async onAdd(map, gl) { | |
this.camera = new THREE.PerspectiveCamera(28, window.innerWidth / window.innerHeight, 0.1, 1e6); | |
// this.camera = new THREE.Camera(); | |
const centerLngLat = map.getCenter(); | |
this.center = MercatorCoordinate.fromLngLat(centerLngLat, 0); | |
const {x, y, z} = this.center; | |
// const s = 1 / this.center.meterInMercatorCoordinateUnits(); | |
this.cameraTransform = new THREE.Matrix4() | |
.makeTranslation(x, y, z) | |
.scale(new THREE.Vector3(1, -1, 1)) | |
// .scale(new THREE.Vector3(s, s, s)); | |
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.autoClear = false; | |
this.raycaster = new THREE.Raycaster(); | |
} | |
makeScene() { | |
const scene = new THREE.Scene(); | |
const skyColor = 0xb1e1ff; // light blue | |
const groundColor = 0xb97a20; // brownish orange | |
scene.add(new THREE.AmbientLight(0xffffff, 0.25)); | |
scene.add(new THREE.HemisphereLight(skyColor, groundColor, 0.25)); | |
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); | |
directionalLight.position.set(-70, -70, 100).normalize(); | |
// Directional lights implicitly point at (0, 0, 0). | |
scene.add(directionalLight); | |
const group = new THREE.Group(); | |
group.name = '$group'; | |
// The models are all in meter coordinates. This shifts them to Mapbox world coordinates. | |
// group.matrix.multiply(this.cameraTransform); | |
group.scale.setScalar(this.center.meterInMercatorCoordinateUnits()); | |
group.updateMatrix(); | |
// const inv = new THREE.Matrix4(); | |
// inv.getInverse(this.cameraTransform); | |
// group.matrix.premultiply(inv); | |
// group.matrixAutoUpdate = false; | |
group.applyMatrix(this.cameraTransform); | |
const geometry = new THREE.BoxGeometry( 100, 100, 100 ); | |
geometry.translate(0, 0, 50); | |
const material = new THREE.MeshPhongMaterial({ | |
color: 0xff0000, | |
}); | |
const cube = new THREE.Mesh( geometry, material ); | |
group.add(cube); | |
scene.add(group); | |
console.log(scene); | |
return scene; | |
} | |
render(gl, viewProjectionMatrix) { | |
const transform = this.map.transform; | |
const camera = this.camera; | |
const projectionMatrix = new Float64Array(16), | |
projectionMatrixI = new Float64Array(16), | |
viewMatrix = new Float64Array(16), | |
viewMatrixI = new Float64Array(16); | |
// from https://github.com/mapbox/mapbox-gl-js/blob/master/src/geo/transform.js#L556-L568 | |
const halfFov = transform._fov / 2; | |
const groundAngle = Math.PI / 2 + transform._pitch; | |
const topHalfSurfaceDistance = Math.sin(halfFov) * transform.cameraToCenterDistance / Math.sin(Math.PI - groundAngle - halfFov); | |
const furthestDistance = Math.cos(Math.PI / 2 - transform._pitch) * topHalfSurfaceDistance + transform.cameraToCenterDistance; | |
const farZ = furthestDistance * 1.01; | |
mat4.perspective(projectionMatrix, transform._fov, transform.width / transform.height, 1, farZ); | |
mat4.invert(projectionMatrixI, projectionMatrix); | |
mat4.multiply(viewMatrix, projectionMatrixI, viewProjectionMatrix); | |
mat4.invert(viewMatrixI, viewMatrix); | |
camera.projectionMatrix = new THREE.Matrix4().fromArray(projectionMatrix); | |
camera.matrix = new THREE.Matrix4().fromArray(viewMatrixI); | |
camera.matrix.decompose(camera.position, camera.quaternion, camera.scale); | |
const prod = camera.matrix.clone(); | |
prod.multiply(camera.projectionMatrix); | |
console.log(prod.elements); | |
console.log(viewProjectionMatrix); | |
// console.log(camera.position) | |
// camera.projectionMatrix = new THREE.Matrix4().fromArray(viewProjectionMatrix); | |
// camera.matrix = new THREE.Matrix4(); | |
// camera.matrix.decompose(camera.position, camera.quaternion, camera.scale); | |
this.renderer.state.reset(); | |
this.renderer.render(this.scene, camera); | |
} | |
raycast(point) { | |
var mouse = new THREE.Vector2(); | |
// scale mouse pixel position to a percentage of the screen's width and height | |
mouse.x = ( point.x / this.map.transform.width ) * 2 - 1; | |
mouse.y = 1 - ( point.y / this.map.transform.height ) * 2; | |
this.raycaster.setFromCamera(mouse, this.camera); | |
// calculate objects intersecting the picking ray | |
var intersects = this.raycaster.intersectObjects(this.scene.children, true); | |
if (intersects.length) { | |
console.log(intersects); | |
} | |
} | |
} | |
let boxLayer = new BoxCustomLayer('box') | |
map.on('load', () => { | |
map.addLayer(boxLayer); | |
}); | |
map.on('click', e => { | |
boxLayer.raycast(e.point); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment