Start working on UI
This commit is contained in:
parent
07208177fd
commit
8227b4141a
16 changed files with 701 additions and 581 deletions
84
app/components/Form.tsx
Normal file
84
app/components/Form.tsx
Normal file
|
@ -0,0 +1,84 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
Button,
|
||||
Checkbox,
|
||||
Label,
|
||||
TextInput,
|
||||
ToggleSwitch,
|
||||
} from "flowbite-react";
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
import {
|
||||
MapSceneContext,
|
||||
MapSceneContextType,
|
||||
} from "../providers/map-scene-provider";
|
||||
import { Mesh, MeshStandardMaterial } from "three";
|
||||
|
||||
export function Form() {
|
||||
const [enabled, setEnabled] = useState<boolean>(true);
|
||||
const { mapScene } = useContext(MapSceneContext) as MapSceneContextType;
|
||||
|
||||
function handleChange() {
|
||||
if (!mapScene) return;
|
||||
|
||||
mapScene.toggleClippingBox();
|
||||
setEnabled(!enabled);
|
||||
}
|
||||
|
||||
function handleCheckboxChange(name: string) {
|
||||
if (!mapScene) return;
|
||||
|
||||
const mesh = mapScene.model.getObjectByName(name);
|
||||
if (mesh) {
|
||||
mesh.visible = !mesh.visible;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-2 overflow-y-auto">
|
||||
<div className="w-full flex flex-col gap-3 p-4 border border-gray-200 rounded shadow">
|
||||
<ToggleSwitch
|
||||
checked={enabled}
|
||||
label="Toggle Slicing Box"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Accordion>
|
||||
<Accordion.Panel>
|
||||
<Accordion.Title>Layers</Accordion.Title>
|
||||
<Accordion.Content>
|
||||
<div className="mt-2">
|
||||
{mapScene?.model.children.map((child) => {
|
||||
const key = `toggle-visibility-${child.name}`;
|
||||
const color = `#${(
|
||||
(child as Mesh).material as MeshStandardMaterial
|
||||
).color.getHexString()}`;
|
||||
return (
|
||||
<div key={key} className="flex items-center ml-2">
|
||||
<span
|
||||
className="inline-block w-4 h-4"
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
}}
|
||||
></span>
|
||||
<Checkbox
|
||||
id={key}
|
||||
defaultChecked
|
||||
onChange={() => handleCheckboxChange(child.name)}
|
||||
className="ml-2"
|
||||
/>
|
||||
<Label htmlFor={key} className="ml-2">
|
||||
{child.name}
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Accordion.Content>
|
||||
</Accordion.Panel>
|
||||
</Accordion>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,17 +1,31 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { init } from "../three/utils/init";
|
||||
import { useContext, useEffect, useRef } from "react";
|
||||
import { MapScene } from "../three/MapScene";
|
||||
import {
|
||||
MapSceneContext,
|
||||
MapSceneContextType,
|
||||
} from "../providers/map-scene-provider";
|
||||
|
||||
export function Map() {
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
const { setMapScene } = useContext(MapSceneContext) as MapSceneContextType;
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
if (!divRef.current) return;
|
||||
|
||||
async function loadScene() {
|
||||
if (divRef.current) {
|
||||
const _mapScene = await MapScene.create(divRef.current, "20");
|
||||
if (_mapScene) {
|
||||
setMapScene(_mapScene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignore) {
|
||||
init(divRef.current);
|
||||
loadScene();
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
|
14
app/page.tsx
14
app/page.tsx
|
@ -1,9 +1,21 @@
|
|||
import { Map } from "./components/Map";
|
||||
import { Form } from "./components/Form";
|
||||
import { MapSceneProvider } from "./providers/map-scene-provider";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="w-screen h-screen">
|
||||
<main className="h-screen">
|
||||
<Map></Map>
|
||||
<MapSceneProvider>
|
||||
<div className="flex h-full">
|
||||
<div className="flex-1">
|
||||
<Map></Map>
|
||||
</div>
|
||||
<div className="w-[480px] p-4 flex flex-col items-center">
|
||||
<Form></Form>
|
||||
</div>
|
||||
</div>
|
||||
</MapSceneProvider>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
|
36
app/providers/map-scene-provider.tsx
Normal file
36
app/providers/map-scene-provider.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
createContext,
|
||||
Dispatch,
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
import { MapScene } from "../three/MapScene";
|
||||
|
||||
// Declare MapScene context
|
||||
export type MapSceneContextType = {
|
||||
mapScene: MapScene | null;
|
||||
setMapScene: Dispatch<SetStateAction<MapScene | null>>;
|
||||
};
|
||||
|
||||
// Context for MapScene
|
||||
export const MapSceneContext = createContext<MapSceneContextType | null>(null);
|
||||
|
||||
// Context provider for MapScene
|
||||
export const MapSceneProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [mapScene, setMapScene] = useState<MapScene | null>(null);
|
||||
|
||||
return (
|
||||
<MapSceneContext.Provider
|
||||
value={{
|
||||
mapScene: mapScene,
|
||||
setMapScene: setMapScene,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</MapSceneContext.Provider>
|
||||
);
|
||||
};
|
105
app/three/MapScene.ts
Normal file
105
app/three/MapScene.ts
Normal file
|
@ -0,0 +1,105 @@
|
|||
import { AxesHelper, Group, Scene } from "three";
|
||||
import { buildMeshes } from "./utils/build-meshes";
|
||||
import { Extent, buildScene } from "./utils/build-scene";
|
||||
import { getMetadata } from "./utils/utils";
|
||||
import { MODEL_ID, SERVICE_URL } from "./config";
|
||||
import { buildClippingplanes } from "./utils/build-clipping-planes";
|
||||
import { buildGrid } from "./utils/build-grid";
|
||||
import { DragControls } from "three/examples/jsm/Addons.js";
|
||||
|
||||
export class MapScene {
|
||||
private _scene: Scene;
|
||||
private _dragControls: DragControls;
|
||||
private _model: Group;
|
||||
|
||||
constructor(scene: Scene, model: Group, dragControls: DragControls) {
|
||||
this._scene = scene;
|
||||
this._dragControls = dragControls;
|
||||
this._model = model;
|
||||
}
|
||||
|
||||
static async create(container: HTMLElement, modelId: string) {
|
||||
const { scene, model, dragControls } = await init(container, modelId);
|
||||
return new MapScene(scene, model, dragControls);
|
||||
}
|
||||
|
||||
get scene() {
|
||||
return this._scene;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
toggleClippingBox() {
|
||||
const box = this._scene?.getObjectByName("clipping-box");
|
||||
if (box) {
|
||||
// Set DragControls
|
||||
if (box.visible) {
|
||||
this._dragControls.enabled = false;
|
||||
} else {
|
||||
this._dragControls.enabled = true;
|
||||
}
|
||||
|
||||
box.visible = !box.visible;
|
||||
}
|
||||
}
|
||||
|
||||
toggleLayerVisibility(layerName: string) {
|
||||
const mesh = this._model.getObjectByName(layerName);
|
||||
if (mesh) {
|
||||
mesh.visible = !mesh.visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function init(container: HTMLElement, modelId = MODEL_ID) {
|
||||
const modelData = await getMetadata(SERVICE_URL + modelId);
|
||||
const mappedFeatures = modelData.mappedfeatures;
|
||||
const modelarea = modelData.modelarea;
|
||||
|
||||
const extent: Extent = {
|
||||
xmin: modelarea.x.min,
|
||||
xmax: modelarea.x.max,
|
||||
ymin: modelarea.y.min,
|
||||
ymax: modelarea.y.max,
|
||||
zmin: modelarea.z.min,
|
||||
zmax: modelarea.z.max,
|
||||
};
|
||||
|
||||
const { renderer, scene, camera, controls } = buildScene(container, extent);
|
||||
|
||||
// Build the 3D model
|
||||
const meshes = await buildMeshes(mappedFeatures);
|
||||
const model = new Group();
|
||||
model.add(...meshes);
|
||||
model.name = "geologic-model";
|
||||
scene.add(model);
|
||||
|
||||
// Build the clipping planes and add them to the scene
|
||||
const { planes, dragControls } = buildClippingplanes(
|
||||
renderer,
|
||||
camera,
|
||||
controls,
|
||||
extent,
|
||||
meshes,
|
||||
scene
|
||||
);
|
||||
|
||||
// Add clipping planes to the meshes
|
||||
for (let mesh of meshes) {
|
||||
mesh.material.clippingPlanes = planes;
|
||||
}
|
||||
|
||||
// Add a coordinate grid to the scene
|
||||
const { gridHelper, annotations } = buildGrid(extent);
|
||||
const annotationsGroup = new Group();
|
||||
annotationsGroup.name = "coordinate-grid";
|
||||
annotationsGroup.add(...annotations);
|
||||
scene.add(gridHelper, annotationsGroup);
|
||||
|
||||
//const axesHelper = new AxesHelper(5);
|
||||
//scene.add(axesHelper);
|
||||
|
||||
return { scene, model, dragControls };
|
||||
}
|
|
@ -2,7 +2,10 @@ import {
|
|||
BufferAttribute,
|
||||
BufferGeometry,
|
||||
DoubleSide,
|
||||
EdgesGeometry,
|
||||
Group,
|
||||
LineBasicMaterial,
|
||||
LineSegments,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
MeshStandardMaterial,
|
||||
|
@ -15,11 +18,7 @@ import {
|
|||
Vector3,
|
||||
WebGLRenderer,
|
||||
} from "three";
|
||||
import {
|
||||
ConvexGeometry,
|
||||
DragControls,
|
||||
OrbitControls,
|
||||
} from "three/examples/jsm/Addons.js";
|
||||
import { DragControls, OrbitControls } from "three/examples/jsm/Addons.js";
|
||||
import { Extent } from "./build-scene";
|
||||
import earcut from "earcut";
|
||||
|
||||
|
@ -30,11 +29,19 @@ enum Orientation {
|
|||
}
|
||||
|
||||
type PlaneMesh = Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap>;
|
||||
type EdgeMesh = LineSegments<
|
||||
EdgesGeometry<PlaneGeometry>,
|
||||
LineBasicMaterial,
|
||||
Object3DEventMap
|
||||
>;
|
||||
type PlaneMeshMap = {
|
||||
[key in Orientation]: PlaneMesh;
|
||||
};
|
||||
type EdgeMashMap = {
|
||||
[key in Orientation]: EdgeMesh;
|
||||
};
|
||||
|
||||
export function createClippingPlanes(
|
||||
export function buildClippingplanes(
|
||||
renderer: WebGLRenderer,
|
||||
camera: PerspectiveCamera,
|
||||
orbitControls: OrbitControls,
|
||||
|
@ -60,13 +67,13 @@ export function createClippingPlanes(
|
|||
},
|
||||
];
|
||||
|
||||
const planeMeshes: Mesh<
|
||||
PlaneGeometry,
|
||||
MeshBasicMaterial,
|
||||
Object3DEventMap
|
||||
>[] = [];
|
||||
const planeMeshes: PlaneMesh[] = [];
|
||||
const edgeMeshes: EdgeMesh[] = [];
|
||||
const planes: Plane[] = [];
|
||||
let planeMeshMap = {} as Partial<PlaneMeshMap>;
|
||||
const planeMeshMap = {} as Partial<PlaneMeshMap>;
|
||||
const edgeMeshMap = {} as Partial<EdgeMashMap>;
|
||||
|
||||
// Create plane meshes
|
||||
for (let p of planesData) {
|
||||
let name;
|
||||
let planeCenter;
|
||||
|
@ -79,7 +86,7 @@ export function createClippingPlanes(
|
|||
planeCenter = new Vector3(
|
||||
-p.d,
|
||||
extent.ymax - width / 2,
|
||||
extent.zmax - height / 2
|
||||
extent.zmin + height / 2
|
||||
);
|
||||
} else if (p.orientation === Orientation.Y) {
|
||||
name = Orientation.Y;
|
||||
|
@ -88,7 +95,7 @@ export function createClippingPlanes(
|
|||
planeCenter = new Vector3(
|
||||
extent.xmax - width / 2,
|
||||
-p.d,
|
||||
extent.zmax - height / 2
|
||||
extent.zmin + height / 2
|
||||
);
|
||||
} else {
|
||||
name = Orientation.Z;
|
||||
|
@ -101,46 +108,65 @@ export function createClippingPlanes(
|
|||
);
|
||||
}
|
||||
|
||||
// Visual representation of the clipping plane
|
||||
// Plane is given in Hesse normal form
|
||||
// Plane is given in Hesse normal form: a * x + b* y + c * y + d = 0, where normal = (a, b, c) and d = d
|
||||
const plane = new Plane(p.normal, p.d);
|
||||
|
||||
// Dragging Mechanism
|
||||
// Visual representation of the clipping plane
|
||||
const planeGeometry = new PlaneGeometry(width, height);
|
||||
const planeMesh = new Mesh(
|
||||
new PlaneGeometry(width, height),
|
||||
planeGeometry,
|
||||
new MeshBasicMaterial({
|
||||
visible: true,
|
||||
color: 0xff0000,
|
||||
color: 0xa92a4e,
|
||||
transparent: true,
|
||||
opacity: 0.1,
|
||||
side: DoubleSide,
|
||||
clipIntersection: false,
|
||||
})
|
||||
);
|
||||
planeMesh.name = name;
|
||||
planeMesh.userData.plane = plane;
|
||||
|
||||
// Create the edges geometry
|
||||
const edgesGeometry = new EdgesGeometry(planeGeometry);
|
||||
const edgesMaterial = new LineBasicMaterial({ color: 0xa92a4e });
|
||||
const edges = new LineSegments(edgesGeometry, edgesMaterial);
|
||||
|
||||
// Translate meshes
|
||||
planeMesh.position.set(planeCenter.x, planeCenter.y, planeCenter.z);
|
||||
edges.position.set(planeCenter.x, planeCenter.y, planeCenter.z);
|
||||
|
||||
// Rotate meshes
|
||||
if (p.orientation === Orientation.X) {
|
||||
planeMesh.rotateY(Math.PI / 2);
|
||||
planeMesh.rotateZ(Math.PI / 2);
|
||||
edges.rotateY(Math.PI / 2);
|
||||
edges.rotateZ(Math.PI / 2);
|
||||
} else if (p.orientation === Orientation.Y) {
|
||||
planeMesh.rotateX(Math.PI / 2);
|
||||
edges.rotateX(Math.PI / 2);
|
||||
}
|
||||
|
||||
planeMeshes.push(planeMesh);
|
||||
edgeMeshes.push(edges);
|
||||
planes.push(plane);
|
||||
|
||||
planeMeshMap[p.orientation] = planeMesh;
|
||||
edgeMeshMap[p.orientation] = edges;
|
||||
}
|
||||
|
||||
for (let pm of planeMeshes) {
|
||||
// Let clipping planes clip each other
|
||||
const clippingPlanes = planes.filter(
|
||||
(p) => !p.normal.equals(pm.userData.plane.normal)
|
||||
);
|
||||
// Add meshes to the scene
|
||||
const planeMeshGroup = new Group();
|
||||
planeMeshGroup.name = "clipping-planes";
|
||||
planeMeshGroup.add(...planeMeshes);
|
||||
|
||||
pm.material.clippingPlanes = clippingPlanes;
|
||||
}
|
||||
const edgeMeshGroup = new Group();
|
||||
edgeMeshGroup.name = "clipping-plane-edges";
|
||||
edgeMeshGroup.add(...edgeMeshes);
|
||||
|
||||
const clippingBox = new Group();
|
||||
clippingBox.add(planeMeshGroup, edgeMeshGroup);
|
||||
clippingBox.name = "clipping-box";
|
||||
scene.add(clippingBox);
|
||||
|
||||
// Enable DragControls for the clipping planes
|
||||
const dragControls = new DragControls(
|
||||
|
@ -149,15 +175,17 @@ export function createClippingPlanes(
|
|||
renderer.domElement
|
||||
);
|
||||
|
||||
dragControls.addEventListener("dragstart", () => {
|
||||
dragControls.addEventListener("dragstart", (event) => {
|
||||
const object = event.object as PlaneMesh;
|
||||
// Disable OrbitControls when dragging starts
|
||||
orbitControls.enabled = false;
|
||||
|
||||
// Remove existing cap meshes
|
||||
let capMeshGroup = scene.getObjectByName("cap-mesh-group");
|
||||
const capMeshGroupName = `cap-mesh-group-${object.name}`;
|
||||
let capMeshGroup = scene.getObjectByName(capMeshGroupName);
|
||||
while (capMeshGroup) {
|
||||
scene.remove(capMeshGroup);
|
||||
capMeshGroup = scene.getObjectByName("cap-mesh-group");
|
||||
capMeshGroup = scene.getObjectByName(capMeshGroupName);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -171,7 +199,9 @@ export function createClippingPlanes(
|
|||
const plane = event.object.userData.plane;
|
||||
const width = object.geometry.parameters.width;
|
||||
const height = object.geometry.parameters.height;
|
||||
let orientation: Orientation;
|
||||
if (object.name === Orientation.Z) {
|
||||
orientation = Orientation.Z;
|
||||
// Fix rotation of dragged mesh
|
||||
event.object.rotation.set(0, 0, 0);
|
||||
|
||||
|
@ -187,14 +217,28 @@ export function createClippingPlanes(
|
|||
// Reset position of plane
|
||||
plane.constant = newZ;
|
||||
|
||||
// Set position of dragged mesh
|
||||
// Set position of dragged meshes
|
||||
object.position.x = extent.xmax - width / 2;
|
||||
object.position.y = extent.ymax - height / 2;
|
||||
object.position.z = newZ;
|
||||
|
||||
// Resize other meshes
|
||||
resizeMeshes(Orientation.Z, newZ, planeMeshMap as PlaneMeshMap, extent);
|
||||
const edgeMesh = edgeMeshMap[Orientation.Z];
|
||||
if (edgeMesh) {
|
||||
edgeMesh.position.x = extent.xmax - width / 2;
|
||||
edgeMesh.position.y = extent.ymax - height / 2;
|
||||
edgeMesh.position.z = newZ;
|
||||
}
|
||||
|
||||
// Resize other meshes to disable dragging of clipped surface parts
|
||||
resizeMeshes(
|
||||
Orientation.Z,
|
||||
newZ,
|
||||
planeMeshMap as PlaneMeshMap,
|
||||
edgeMeshMap as EdgeMashMap,
|
||||
extent
|
||||
);
|
||||
} else if (object.name === Orientation.Y) {
|
||||
orientation = Orientation.Y;
|
||||
// Fix rotation of dragged mesh
|
||||
event.object.rotation.set(Math.PI / 2, 0, 0);
|
||||
|
||||
|
@ -213,11 +257,26 @@ export function createClippingPlanes(
|
|||
// Set position of dragged mesh
|
||||
object.position.x = extent.xmax - width / 2;
|
||||
object.position.y = newY;
|
||||
object.position.z = extent.zmax - height / 2;
|
||||
object.position.z = extent.zmin + height / 2;
|
||||
|
||||
const edgeMesh = edgeMeshMap[Orientation.Y];
|
||||
if (edgeMesh) {
|
||||
edgeMesh.position.x = extent.xmax - width / 2;
|
||||
edgeMesh.position.y = newY;
|
||||
edgeMesh.position.z = extent.zmin + height / 2;
|
||||
}
|
||||
|
||||
// Resize other meshes
|
||||
resizeMeshes(Orientation.Y, newY, planeMeshMap as PlaneMeshMap, extent);
|
||||
resizeMeshes(
|
||||
Orientation.Y,
|
||||
newY,
|
||||
planeMeshMap as PlaneMeshMap,
|
||||
edgeMeshMap as EdgeMashMap,
|
||||
extent
|
||||
);
|
||||
} else {
|
||||
orientation = Orientation.X;
|
||||
|
||||
// Fix rotation of dragged mesh
|
||||
event.object.rotation.set(0, Math.PI / 2, Math.PI / 2);
|
||||
|
||||
|
@ -236,124 +295,178 @@ export function createClippingPlanes(
|
|||
// Set position of dragged mesh
|
||||
object.position.x = newX;
|
||||
object.position.y = extent.ymax - width / 2;
|
||||
object.position.z = extent.zmax - height / 2;
|
||||
object.position.z = extent.zmin + height / 2;
|
||||
|
||||
const edgeMesh = edgeMeshMap[Orientation.X];
|
||||
if (edgeMesh) {
|
||||
edgeMesh.position.x = newX;
|
||||
edgeMesh.position.y = extent.ymax - width / 2;
|
||||
edgeMesh.position.z = extent.zmin + height / 2;
|
||||
}
|
||||
|
||||
// Resize other meshes
|
||||
resizeMeshes(Orientation.X, newX, planeMeshMap as PlaneMeshMap, extent);
|
||||
resizeMeshes(
|
||||
Orientation.X,
|
||||
newX,
|
||||
planeMeshMap as PlaneMeshMap,
|
||||
edgeMeshMap as EdgeMashMap,
|
||||
extent
|
||||
);
|
||||
}
|
||||
|
||||
// Remove existing cap meshes
|
||||
let capMeshGroup = scene.getObjectByName("cap-mesh-group");
|
||||
const capMeshGroupName = `cap-mesh-group-${object.name}`;
|
||||
let capMeshGroup = scene.getObjectByName(capMeshGroupName);
|
||||
while (capMeshGroup) {
|
||||
scene.remove(capMeshGroup);
|
||||
capMeshGroup = scene.getObjectByName("cap-mesh-group");
|
||||
capMeshGroup = scene.getObjectByName(capMeshGroupName);
|
||||
}
|
||||
|
||||
const capMeshes = generateCapMeshes(meshes, plane);
|
||||
// Generate new cap meshes
|
||||
const capMeshes = generateCapMeshes(meshes, plane, planes, orientation);
|
||||
|
||||
// Add new cap meshes
|
||||
if (capMeshes.length > 0) {
|
||||
// Add new cap meshes
|
||||
const newCapMeshGroup = new Group();
|
||||
|
||||
newCapMeshGroup.add(...capMeshes);
|
||||
newCapMeshGroup.name = "cap-mesh-group";
|
||||
newCapMeshGroup.name = capMeshGroupName;
|
||||
scene.add(newCapMeshGroup);
|
||||
}
|
||||
});
|
||||
|
||||
return { planeMeshes, planes };
|
||||
return { planes, dragControls };
|
||||
}
|
||||
|
||||
function resizeMeshes(
|
||||
orientation: Orientation,
|
||||
newCoordinate: number,
|
||||
planeMeshes: PlaneMeshMap,
|
||||
edgeMeshes: EdgeMashMap,
|
||||
extent: Extent
|
||||
) {
|
||||
if (orientation === Orientation.X) {
|
||||
// Resize y-clipping plane
|
||||
let planeMesh = planeMeshes[Orientation.Y];
|
||||
let edgeMesh = edgeMeshes[Orientation.Y];
|
||||
let width = extent.xmax - newCoordinate;
|
||||
let height = planeMesh.geometry.parameters.height;
|
||||
let planeGeometry = new PlaneGeometry(width, height);
|
||||
const y = planeMesh.position.y;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.geometry = planeGeometry;
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
y,
|
||||
extent.zmax - height / 2
|
||||
extent.zmin + height / 2
|
||||
);
|
||||
edgeMesh.geometry.dispose();
|
||||
edgeMesh.geometry = new EdgesGeometry(planeGeometry);
|
||||
edgeMesh.position.set(extent.xmax - width / 2, y, extent.zmin + height / 2);
|
||||
|
||||
// Resize z-clipping-plane
|
||||
planeMesh = planeMeshes[Orientation.Z];
|
||||
edgeMesh = edgeMeshes[Orientation.Z];
|
||||
width = extent.xmax - newCoordinate;
|
||||
height = planeMesh.geometry.parameters.height;
|
||||
planeGeometry = new PlaneGeometry(width, height);
|
||||
const z = planeMesh.position.z;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.geometry = planeGeometry;
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
extent.ymax - height / 2,
|
||||
z
|
||||
);
|
||||
edgeMesh.geometry.dispose();
|
||||
edgeMesh.geometry = new EdgesGeometry(planeGeometry);
|
||||
edgeMesh.position.set(extent.xmax - width / 2, extent.ymax - height / 2, z);
|
||||
} else if (orientation === Orientation.Y) {
|
||||
// Resize x-clipping plane
|
||||
let planeMesh = planeMeshes[Orientation.X];
|
||||
let edgeMesh = edgeMeshes[Orientation.X];
|
||||
let width = extent.ymax - newCoordinate;
|
||||
let height = planeMesh.geometry.parameters.height;
|
||||
let planeGeometry = new PlaneGeometry(width, height);
|
||||
const x = planeMesh.position.x;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.geometry = planeGeometry;
|
||||
planeMesh.position.set(
|
||||
x,
|
||||
extent.ymax - width / 2,
|
||||
extent.zmax - height / 2
|
||||
extent.zmin + height / 2
|
||||
);
|
||||
edgeMesh.geometry.dispose();
|
||||
edgeMesh.geometry = new EdgesGeometry(planeGeometry);
|
||||
edgeMesh.position.set(x, extent.ymax - width / 2, extent.zmin + height / 2);
|
||||
|
||||
// Resize z-clipping-plane
|
||||
planeMesh = planeMeshes[Orientation.Z];
|
||||
edgeMesh = edgeMeshes[Orientation.Z];
|
||||
width = planeMesh.geometry.parameters.width;
|
||||
height = extent.ymax - newCoordinate;
|
||||
planeGeometry = new PlaneGeometry(width, height);
|
||||
const z = planeMesh.position.z;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.geometry = planeGeometry;
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
extent.ymax - height / 2,
|
||||
z
|
||||
);
|
||||
edgeMesh.geometry.dispose();
|
||||
edgeMesh.geometry = new EdgesGeometry(planeGeometry);
|
||||
edgeMesh.position.set(extent.xmax - width / 2, extent.ymax - height / 2, z);
|
||||
} else if (orientation === Orientation.Z) {
|
||||
// Resize x-clipping-plane
|
||||
let planeMesh = planeMeshes[Orientation.X];
|
||||
let edgeMesh = edgeMeshes[Orientation.X];
|
||||
let width = planeMesh.geometry.parameters.width;
|
||||
let height = newCoordinate - extent.zmin;
|
||||
let planeGeometry = new PlaneGeometry(width, height);
|
||||
const x = planeMesh.position.x;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.geometry = planeGeometry;
|
||||
planeMesh.position.set(
|
||||
x,
|
||||
extent.ymax - width / 2,
|
||||
extent.zmax - height / 2
|
||||
extent.zmin + height / 2
|
||||
);
|
||||
edgeMesh.geometry.dispose();
|
||||
edgeMesh.geometry = new EdgesGeometry(planeGeometry);
|
||||
edgeMesh.position.set(x, extent.ymax - width / 2, extent.zmin + height / 2);
|
||||
|
||||
// Resize y-clipping plane
|
||||
planeMesh = planeMeshes[Orientation.Y];
|
||||
edgeMesh = edgeMeshes[Orientation.Y];
|
||||
width = planeMesh.geometry.parameters.width;
|
||||
height = newCoordinate - extent.zmin;
|
||||
planeGeometry = new PlaneGeometry(width, height);
|
||||
const y = planeMesh.position.y;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.geometry = planeGeometry;
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
y,
|
||||
extent.zmax - height / 2
|
||||
extent.zmin + height / 2
|
||||
);
|
||||
|
||||
edgeMesh.geometry.dispose();
|
||||
edgeMesh.geometry = new EdgesGeometry(planeGeometry);
|
||||
edgeMesh.position.set(extent.xmax - width / 2, y, extent.zmin + height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract contour and generate cap
|
||||
function generateCapMeshes(meshes: Mesh[], plane: Plane) {
|
||||
function generateCapMeshes(
|
||||
meshes: Mesh[],
|
||||
plane: Plane,
|
||||
planes: Plane[],
|
||||
orientation: Orientation
|
||||
) {
|
||||
const capMeshes: Mesh[] = [];
|
||||
|
||||
// Iterate over the list of geologic meshes
|
||||
for (let mesh of meshes) {
|
||||
const position = mesh.geometry.attributes.position.array;
|
||||
const indices = mesh.geometry.index ? mesh.geometry.index.array : null;
|
||||
|
@ -389,14 +502,20 @@ function generateCapMeshes(meshes: Mesh[], plane: Plane) {
|
|||
}
|
||||
}
|
||||
|
||||
// Intersection surface can be a multipolygon consisting of disconnected polygons
|
||||
const polygons: Vector3[][] = buildPolygons(edges);
|
||||
|
||||
// Clip cap surfaces with clipping planes
|
||||
const clippingPlanes = planes.filter((p) => !p.normal.equals(plane.normal));
|
||||
|
||||
const offset = orientation === Orientation.Z ? 1 : -1;
|
||||
const material = new MeshStandardMaterial({
|
||||
color: (mesh.material as MeshStandardMaterial).color,
|
||||
side: DoubleSide,
|
||||
polygonOffset: true,
|
||||
polygonOffsetFactor: -1,
|
||||
polygonOffsetUnits: -1,
|
||||
polygonOffsetFactor: offset,
|
||||
polygonOffsetUnits: offset,
|
||||
clippingPlanes,
|
||||
});
|
||||
|
||||
const localMeshes = polygons.map((polygon) => {
|
||||
|
@ -405,7 +524,6 @@ function generateCapMeshes(meshes: Mesh[], plane: Plane) {
|
|||
const capMesh = new Mesh(geometry, material);
|
||||
|
||||
// Offset mesh to avoid flickering
|
||||
const offset = -1;
|
||||
const normal = plane.normal.clone().multiplyScalar(offset);
|
||||
|
||||
const positionAttr = capMesh.geometry.attributes.position;
|
||||
|
@ -518,13 +636,6 @@ function triangulatePolygon(vertices: Vector3[], plane: Plane) {
|
|||
return geometry;
|
||||
}
|
||||
|
||||
// Compute the centroid of a list of 2D vertices
|
||||
function computeCentroid(vertices: Vector2[]): Vector2 {
|
||||
const centroid = new Vector2();
|
||||
vertices.forEach((v) => centroid.add(v));
|
||||
return centroid.divideScalar(vertices.length);
|
||||
}
|
||||
|
||||
// Function to find the intersection point between an edge and a plane
|
||||
function intersectEdge(v1: Vector3, v2: Vector3, d1: number, d2: number) {
|
||||
const t = d1 / (d1 - d2);
|
|
@ -4,12 +4,8 @@ import {
|
|||
DoubleSide,
|
||||
Mesh,
|
||||
MeshStandardMaterial,
|
||||
Plane,
|
||||
} from "three";
|
||||
|
||||
import { uniforms } from "./uniforms";
|
||||
import { shader } from "./shader";
|
||||
|
||||
import { fetchTriangleIndices } from "./fetch-triangle-indices";
|
||||
import { fetchVertices } from "./fetch-vertices";
|
||||
import { TRIANGLE_INDICES_URL, VERTICES_URL } from "../config";
|
||||
|
@ -21,6 +17,17 @@ interface MappedFeature {
|
|||
preview: { legend_color: string; legend_text: string };
|
||||
}
|
||||
|
||||
export async function buildMeshes(mappedFeatures: MappedFeature[]) {
|
||||
const meshes = [];
|
||||
for (let i = 0; i < mappedFeatures.length; i++) {
|
||||
const layerData = mappedFeatures[i];
|
||||
const mesh = await buildMesh(layerData);
|
||||
meshes.push(mesh);
|
||||
}
|
||||
|
||||
return meshes;
|
||||
}
|
||||
|
||||
async function buildMesh(layerData: MappedFeature) {
|
||||
const color = `#${layerData.preview.legend_color}`;
|
||||
const name = layerData.preview.legend_text;
|
||||
|
@ -43,23 +50,13 @@ async function buildMesh(layerData: MappedFeature) {
|
|||
|
||||
const material = new MeshStandardMaterial({
|
||||
color: color,
|
||||
metalness: 0.1,
|
||||
metalness: 0.0,
|
||||
roughness: 0.75,
|
||||
flatShading: true,
|
||||
side: DoubleSide,
|
||||
wireframe: false,
|
||||
clipIntersection: false,
|
||||
});
|
||||
|
||||
// material.onBeforeCompile = (materialShader) => {
|
||||
// materialShader.uniforms.clippingLow = uniforms.clipping.clippingLow;
|
||||
// materialShader.uniforms.clippingHigh = uniforms.clipping.clippingHigh;
|
||||
// materialShader.uniforms.clippingScale = uniforms.clipping.clippingScale;
|
||||
|
||||
// materialShader.vertexShader = shader.vertexMeshStandard;
|
||||
// materialShader.fragmentShader = shader.fragmentClippingMeshStandard;
|
||||
// };
|
||||
|
||||
const mesh = new Mesh(geometry, material);
|
||||
mesh.name = name;
|
||||
mesh.userData.layerId = geomId;
|
||||
|
@ -68,16 +65,3 @@ async function buildMesh(layerData: MappedFeature) {
|
|||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
export async function buildMeshes(mappedFeatures: MappedFeature[]) {
|
||||
const meshes = [];
|
||||
for (let i = 0; i < mappedFeatures.length; i++) {
|
||||
const layerData = mappedFeatures[i];
|
||||
if (layerData.name !== "Topography") {
|
||||
const mesh = await buildMesh(layerData);
|
||||
meshes.push(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
return meshes;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ let controls: OrbitControls;
|
|||
let renderer: WebGLRenderer;
|
||||
let camera: PerspectiveCamera;
|
||||
let scene: Scene;
|
||||
export async function buildScene(container: HTMLElement, extent: Extent) {
|
||||
export function buildScene(container: HTMLElement, extent: Extent) {
|
||||
const maxSize = getMaxSize(extent);
|
||||
const center = getCenter3D(extent);
|
||||
|
||||
|
@ -31,7 +31,7 @@ export async function buildScene(container: HTMLElement, extent: Extent) {
|
|||
maxSize * 25
|
||||
);
|
||||
|
||||
camera.position.set(center.x, center.y, extent.zmax + 150000);
|
||||
camera.position.set(center.x, center.y - 125000, extent.zmax + 100000);
|
||||
camera.lookAt(center);
|
||||
|
||||
renderer = new WebGLRenderer({
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
let lastId = 0;
|
||||
|
||||
export function stamp(obj: any) {
|
||||
return obj._id ?? (obj._id = ++lastId);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
export async function getMetadata(serviceUrl: string) {
|
||||
const response = await fetch(serviceUrl, {
|
||||
method: "GET",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw new Error("HTTP error status: " + response.status);
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
import { AxesHelper, Group } from "three";
|
||||
import { buildMeshes } from "./build-meshes";
|
||||
import { Extent, buildScene } from "./build-scene";
|
||||
import { getMetadata } from "./get-metadata";
|
||||
import { MODEL_ID, SERVICE_URL } from "../config";
|
||||
import { createClippingPlanes } from "./build-clipping-plane";
|
||||
import { buildGrid } from "./build-grid";
|
||||
|
||||
export async function init(container: HTMLElement) {
|
||||
const modelData = await getMetadata(SERVICE_URL + MODEL_ID);
|
||||
const mappedFeatures = modelData.mappedfeatures;
|
||||
const modelarea = modelData.modelarea;
|
||||
|
||||
const extent: Extent = {
|
||||
xmin: modelarea.x.min,
|
||||
xmax: modelarea.x.max,
|
||||
ymin: modelarea.y.min,
|
||||
ymax: modelarea.y.max,
|
||||
zmin: modelarea.z.min,
|
||||
zmax: modelarea.z.max,
|
||||
};
|
||||
|
||||
const { renderer, scene, camera, controls } = await buildScene(
|
||||
container,
|
||||
extent
|
||||
);
|
||||
|
||||
// Create the 3D model
|
||||
const meshes = await buildMeshes(mappedFeatures);
|
||||
const model = new Group();
|
||||
model.add(...meshes);
|
||||
model.name = "3d-model";
|
||||
scene.add(model);
|
||||
|
||||
// Create the clipping planes and add them to the scene
|
||||
const { planeMeshes, planes } = createClippingPlanes(
|
||||
renderer,
|
||||
camera,
|
||||
controls,
|
||||
extent,
|
||||
meshes,
|
||||
scene
|
||||
);
|
||||
scene.add(...planeMeshes);
|
||||
|
||||
// Add clipping planes to the meshes
|
||||
for (let mesh of meshes) {
|
||||
mesh.material.clippingPlanes = planes;
|
||||
}
|
||||
|
||||
// Add a coordinate grid to the scene
|
||||
const { gridHelper, annotations } = buildGrid(extent);
|
||||
const annotationsGroup = new Group();
|
||||
annotationsGroup.add(...annotations);
|
||||
scene.add(gridHelper, annotationsGroup);
|
||||
|
||||
//const axesHelper = new AxesHelper(5);
|
||||
//scene.add(axesHelper);
|
||||
}
|
|
@ -1,254 +0,0 @@
|
|||
export const shader = {
|
||||
vertex: `
|
||||
uniform vec3 color;
|
||||
varying vec3 pixelNormal;
|
||||
|
||||
void main() {
|
||||
|
||||
pixelNormal = normal;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
vertexClipping: `
|
||||
uniform vec3 color;
|
||||
uniform vec3 clippingLow;
|
||||
uniform vec3 clippingHigh;
|
||||
|
||||
varying vec3 pixelNormal;
|
||||
varying vec4 worldPosition;
|
||||
varying vec3 camPosition;
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vPosition;
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vec4 vPos = modelViewMatrix * vec4( position, 1.0 );
|
||||
vPosition = vPos.xyz;
|
||||
|
||||
vNormal = normalMatrix * normal;
|
||||
pixelNormal = normal;
|
||||
worldPosition = modelMatrix * vec4( position, 1.0 );
|
||||
camPosition = cameraPosition;
|
||||
|
||||
// gl_Position = projectionMatrix * vPos;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||||
|
||||
}`,
|
||||
|
||||
fragment: `
|
||||
uniform vec3 color;
|
||||
varying vec3 pixelNormal;
|
||||
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vPosition;
|
||||
|
||||
// uniform vec3 spotLightPosition; // in world space
|
||||
// uniform vec3 clight;
|
||||
// uniform vec3 cspec;
|
||||
// uniform float roughness;
|
||||
const float PI = 3.14159;
|
||||
|
||||
|
||||
void main( void ) {
|
||||
|
||||
float shade = (
|
||||
3.0 * pow ( abs ( pixelNormal.y ), 2.0 )
|
||||
+ 2.0 * pow ( abs ( pixelNormal.z ), 2.0 )
|
||||
+ 1.0 * pow ( abs ( pixelNormal.x ), 2.0 )
|
||||
) / 3.0;
|
||||
|
||||
gl_FragColor = vec4( color * shade, 1.0 );
|
||||
//gl_FragColor = vec4(color, 1.0);
|
||||
|
||||
// vec4 lPosition = viewMatrix * vec4( spotLightPosition, 1.0 );
|
||||
// vec3 l = normalize(lPosition.xyz - vPosition.xyz);
|
||||
// vec3 n = normalize( vNormal ); // interpolation destroys normalization, so we have to normalize
|
||||
// vec3 v = normalize( -vPosition);
|
||||
// vec3 h = normalize( v + l);
|
||||
|
||||
// // small quantity to prevent divisions by 0
|
||||
// float nDotl = max(dot( n, l ),0.000001);
|
||||
// float lDoth = max(dot( l, h ),0.000001);
|
||||
// float nDoth = max(dot( n, h ),0.000001);
|
||||
// float vDoth = max(dot( v, h ),0.000001);
|
||||
// float nDotv = max(dot( n, v ),0.000001);
|
||||
|
||||
// vec3 specularBRDF = FSchlick(lDoth)*GSmith(nDotv,nDotl)*DGGX(nDoth,roughness*roughness)/(4.0*nDotl*nDotv);
|
||||
// vec3 outRadiance = (PI* clight * nDotl * specularBRDF);
|
||||
|
||||
// gl_FragColor = vec4(pow( outRadiance, vec3(1.0/2.2)), 1.0);
|
||||
|
||||
}`,
|
||||
|
||||
fragmentClippingFront: `
|
||||
uniform sampler2D map;
|
||||
uniform vec3 color;
|
||||
uniform vec3 clippingLow;
|
||||
uniform vec3 clippingHigh;
|
||||
uniform float clippingScale;
|
||||
uniform float percent;
|
||||
|
||||
varying vec3 pixelNormal;
|
||||
varying vec4 worldPosition;
|
||||
varying vec3 camPosition;
|
||||
varying vec2 vUv;
|
||||
|
||||
void main( void ) {
|
||||
|
||||
float shade = (
|
||||
3.0 * pow ( abs ( pixelNormal.y ), 2.0 )
|
||||
+ 2.0 * pow ( abs ( pixelNormal.z ), 2.0 )
|
||||
+ 1.0 * pow ( abs ( pixelNormal.x ), 2.0 )
|
||||
) / 3.0;
|
||||
|
||||
if (
|
||||
worldPosition.x < clippingLow.x
|
||||
|| worldPosition.x > clippingHigh.x
|
||||
|| worldPosition.y < clippingLow.y
|
||||
|| worldPosition.y > clippingHigh.y
|
||||
|| worldPosition.z < (clippingLow.z * clippingScale)
|
||||
|| worldPosition.z > (clippingHigh.z * clippingScale)
|
||||
) {
|
||||
discard;
|
||||
} else {
|
||||
gl_FragColor = texture2D(map, vUv);
|
||||
gl_FragColor.a = percent;
|
||||
}
|
||||
|
||||
}`,
|
||||
|
||||
vertexMeshStandard: `
|
||||
#define STANDARD
|
||||
|
||||
varying vec3 vViewPosition;
|
||||
varying vec4 worldPosition;
|
||||
|
||||
#include <common>
|
||||
#include <uv_pars_vertex>
|
||||
#include <color_pars_vertex>
|
||||
#include <fog_pars_vertex>
|
||||
#include <shadowmap_pars_vertex>
|
||||
#include <logdepthbuf_pars_vertex>
|
||||
#include <clipping_planes_pars_vertex>
|
||||
|
||||
void main() {
|
||||
#include <uv_vertex>
|
||||
#include <color_vertex>
|
||||
|
||||
#include <beginnormal_vertex>
|
||||
#include <defaultnormal_vertex>
|
||||
|
||||
#include <begin_vertex>
|
||||
#include <project_vertex>
|
||||
#include <logdepthbuf_vertex>
|
||||
#include <clipping_planes_vertex>
|
||||
|
||||
vViewPosition = -mvPosition.xyz;
|
||||
|
||||
worldPosition = modelMatrix * vec4(position, 1.0);
|
||||
|
||||
#include <shadowmap_vertex>
|
||||
#include <fog_vertex>
|
||||
}
|
||||
`,
|
||||
|
||||
fragmentClippingMeshStandard: `
|
||||
#define STANDARD
|
||||
|
||||
uniform vec3 diffuse;
|
||||
uniform vec3 emissive;
|
||||
uniform float roughness;
|
||||
uniform float metalness;
|
||||
uniform float opacity;
|
||||
|
||||
varying vec3 vViewPosition;
|
||||
varying vec4 worldPosition;
|
||||
|
||||
uniform vec3 clippingLow;
|
||||
uniform vec3 clippingHigh;
|
||||
uniform float clippingScale;
|
||||
|
||||
#include <common>
|
||||
#include <packing>
|
||||
#include <dithering_pars_fragment>
|
||||
#include <color_pars_fragment>
|
||||
#include <uv_pars_fragment>
|
||||
#include <map_pars_fragment>
|
||||
#include <alphamap_pars_fragment>
|
||||
#include <aomap_pars_fragment>
|
||||
#include <emissivemap_pars_fragment>
|
||||
#include <bsdfs>
|
||||
#include <transmission_pars_fragment>
|
||||
#include <envmap_physical_pars_fragment>
|
||||
#include <fog_pars_fragment>
|
||||
#include <lights_pars_begin>
|
||||
#include <lights_physical_pars_fragment>
|
||||
#include <shadowmap_pars_fragment>
|
||||
#include <normalmap_pars_fragment>
|
||||
#include <roughnessmap_pars_fragment>
|
||||
#include <metalnessmap_pars_fragment>
|
||||
#include <logdepthbuf_pars_fragment>
|
||||
#include <clipping_planes_pars_fragment>
|
||||
|
||||
void main() {
|
||||
#include <clipping_planes_fragment>
|
||||
|
||||
vec4 diffuseColor = vec4(diffuse, opacity);
|
||||
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
|
||||
vec3 totalEmissiveRadiance = emissive;
|
||||
|
||||
#ifdef TRANSMISSION
|
||||
float totalTransmission = transmission;
|
||||
float thicknessFactor = thickness;
|
||||
#endif
|
||||
|
||||
#include <logdepthbuf_fragment>
|
||||
#include <map_fragment>
|
||||
#include <color_fragment>
|
||||
#include <alphamap_fragment>
|
||||
#include <alphatest_fragment>
|
||||
#include <roughnessmap_fragment>
|
||||
#include <metalnessmap_fragment>
|
||||
#include <normal_fragment_begin>
|
||||
#include <normal_fragment_maps>
|
||||
#include <emissivemap_fragment>
|
||||
|
||||
vec3 rawDiffuseColor = diffuseColor.rgb;
|
||||
#include <transmission_fragment>
|
||||
|
||||
// Lighting calculations
|
||||
#include <lights_physical_fragment>
|
||||
#include <lights_fragment_maps>
|
||||
|
||||
// Ambient occlusion
|
||||
#include <aomap_fragment>
|
||||
|
||||
vec3 outgoingLight = reflectedLight.directDiffuse
|
||||
+ reflectedLight.indirectDiffuse
|
||||
+ reflectedLight.directSpecular
|
||||
+ reflectedLight.indirectSpecular
|
||||
+ totalEmissiveRadiance;
|
||||
|
||||
// Clipping logic
|
||||
if (any(greaterThan(worldPosition.xyz, clippingHigh)) || any(lessThan(worldPosition.xyz, clippingLow))) {
|
||||
discard;
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(outgoingLight, diffuseColor.a);
|
||||
}
|
||||
`,
|
||||
|
||||
invisibleVertexShader: `
|
||||
void main() {
|
||||
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
}`,
|
||||
|
||||
invisibleFragmentShader: `
|
||||
void main( void ) {
|
||||
gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
|
||||
discard;
|
||||
}`,
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
import { Vector3, Color } from "three";
|
||||
|
||||
export const uniforms = {
|
||||
clipping: {
|
||||
// light blue
|
||||
color: { value: new Color(0x3d9ecb) },
|
||||
clippingLow: { value: new Vector3(0, 0, 0) },
|
||||
clippingHigh: { value: new Vector3(0, 0, 0) },
|
||||
// additional parameter for scaling
|
||||
clippingScale: { value: 1.0 },
|
||||
// topography
|
||||
map: { value: null },
|
||||
percent: { value: 1 },
|
||||
},
|
||||
caps: {
|
||||
// red
|
||||
color: { value: new Color(0xf83610) },
|
||||
},
|
||||
};
|
|
@ -16,3 +16,19 @@ export function getCenter3D(extent: Extent) {
|
|||
(extent.zmax + extent.zmin) / 2
|
||||
);
|
||||
}
|
||||
|
||||
export async function getMetadata(serviceUrl: string) {
|
||||
const response = await fetch(serviceUrl, {
|
||||
method: "GET",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw new Error("HTTP error status: " + response.status);
|
||||
}
|
||||
}
|
||||
|
|
371
package-lock.json
generated
371
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"earcut": "^3.0.1",
|
||||
"flowbite-react": "^0.10.2",
|
||||
"next": "15.1.7",
|
||||
"proj4": "^2.15.0",
|
||||
"react": "^19.0.0",
|
||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue