Skip to content

Commit

Permalink
add gtpf loader
Browse files Browse the repository at this point in the history
  • Loading branch information
gcor committed Oct 20, 2023
1 parent 3288dc8 commit 57acbc1
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 202 deletions.
102 changes: 102 additions & 0 deletions components/Layers/Model/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* eslint-disable import/no-extraneous-dependencies */
import * as THREE from 'three';
import maplibregl from 'maplibre-gl';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { COORDS_EKATERINBURG } from 'constants/coords';

// parameters to ensure the model is georeferenced correctly on the map
const modelOrigin = COORDS_EKATERINBURG;
const modelAltitude = 0;
const modelRotate = [Math.PI / 2, 0, 0];

const modelAsMercatorCoordinate = maplibregl.MercatorCoordinate.fromLngLat(
modelOrigin,
modelAltitude,
);

// transformation parameters to position, rotate and scale the 3D model onto the map
const modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
rotateX: modelRotate[0],
rotateY: modelRotate[1],
rotateZ: modelRotate[2],
/* Since our 3D model is in real world meters, a scale transform needs to be
* applied since the CustomLayerInterface expects units in MercatorCoordinates.
*/
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits(),
};

// configuration of the custom layer for a 3D model per the CustomLayerInterface
export const getModelLayer = (id: string, path: string) => ({
id,
type: 'custom',
renderingMode: '3d',
onAdd(map, gl) {
this.camera = new THREE.Camera();
this.scene = new THREE.Scene();

// create two three.js lights to illuminate the model
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, -70, 100).normalize();
this.scene.add(directionalLight);

const 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
const loader = new GLTFLoader();
loader.load(path, (gltf) => {
this.scene.add(gltf.scene);
});
this.map = map;

// use the MapLibre GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
});

this.renderer.autoClear = false;
},
render(gl, matrix) {
const rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
modelTransform.rotateX,
);
const rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
modelTransform.rotateY,
);
const rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
modelTransform.rotateZ,
);

const m = new THREE.Matrix4().fromArray(matrix);
const 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);

this.camera.projectionMatrix = m.multiply(l);
this.renderer.resetState();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
},
});
4 changes: 3 additions & 1 deletion components/Map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { DesignCodeSource } from './layers/DesignCodeSource';
import { MapContext } from './providers/MapProvider';

import 'maplibre-gl/dist/maplibre-gl.css';
import { ModelSource } from './layers/ModelSource';

function MapLayers() {
return (
Expand All @@ -23,6 +24,7 @@ function MapLayers() {
<DtpSource />
<LinesSource />
<DesignCodeSource />
<ModelSource />
</>
);
}
Expand All @@ -40,7 +42,7 @@ export function Map() {
pitch: 30,
}}
minZoom={11}
maxZoom={20}
maxZoom={23}
// hash
style={{ width: '100vw', height: '100vh', color: 'black' }}
mapStyle="https://map-backend.netlify.app/style.json"
Expand Down
24 changes: 24 additions & 0 deletions components/Map/layers/ModelSource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { useMap } from 'react-map-gl';
import { getModelLayer } from 'components/Layers/Model';

export function ModelSource() {
const ekbMap = useMap();

return (
<input
style={{ position: 'absolute', top: 0, left: 0, zIndex: 1000 }}
type="file"
onChange={(e) => {
const map = ekbMap?.current?.getMap?.();
map.addLayer(
// @ts-ignore
getModelLayer(
`3d-model-${Math.round(Math.random() * 1000)}`,
URL.createObjectURL(e.target.files[0]),
),
);
}}
/>
);
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
"@types/lodash": "^4.14.197",
"@types/node": "20.5.4",
"@types/react": "^18.2.21",
"@types/three": "^0.155.1",
"classnames": "^2.3.2",
"ekb": "^1.1.4",
"framer-motion": "^10.16.1",
"hls.js": "^1.4.10",
"lodash": "^4.17.21",
"mapbox-gl": "npm:[email protected]",
"maplibre-gl": "^3.3.0",
Expand All @@ -36,6 +36,7 @@
"react-modal-sheet": "^2.0.0",
"react-redux": "^8.1.2",
"redux": "^4.2.1",
"three": "^0.156.0",
"typescript": "^5.1.6"
},
"devDependencies": {
Expand Down
Loading

1 comment on commit 57acbc1

@ekbdev
Copy link

@ekbdev ekbdev commented on 57acbc1 Oct 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for map ready!

✅ Preview
https://map-eg8lz7ncj-ekbdev.vercel.app
https://ekbdev-map-gltf.vercel.app

Built with commit 57acbc1.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.