Add terrain heights

This commit is contained in:
Fuhrmann 2025-03-13 15:11:08 +01:00
parent 3e6504d6b0
commit d2987d07c4
5 changed files with 108 additions and 98 deletions

View file

@ -118,10 +118,7 @@ export function Form() {
function handleCheckboxChange(name: string) { function handleCheckboxChange(name: string) {
if (!sceneView) return; if (!sceneView) return;
const mesh = sceneView.model.getObjectByName(name); sceneView.toggleLayerVisibility(name);
if (mesh) {
mesh.visible = !mesh.visible;
}
} }
function handleChangeTopography() { function handleChangeTopography() {

View file

@ -84,9 +84,6 @@ export class CustomMapHeightNodeShader extends MapHeightNode {
new MeshPhongMaterial({ new MeshPhongMaterial({
map: MapNode.defaultTexture, map: MapNode.defaultTexture,
color: 0xffffff, color: 0xffffff,
transparent: true,
opacity: 1.0,
depthTest: true,
}) })
); );
@ -109,7 +106,7 @@ export class CustomMapHeightNodeShader extends MapHeightNode {
material material
); );
this.frustumCulled = false; this.frustumCulled = true;
} }
/** /**
@ -147,9 +144,10 @@ export class CustomMapHeightNodeShader extends MapHeightNode {
vec4 _theight = texture(heightMap, vMapUv); vec4 _theight = texture(heightMap, vMapUv);
float _height = ((_theight.r * 255.0 * 65536.0 + _theight.g * 255.0 * 256.0 + _theight.b * 255.0) * 0.1) - 10000.0; float _height = ((_theight.r * 255.0 * 65536.0 + _theight.g * 255.0 * 256.0 + _theight.b * 255.0) * 0.1) - 10000.0;
// Apply height displacement
vec3 _transformed = position + _height * normal; vec3 _transformed = position + _height * normal;
// Vertex position based on height
gl_Position = projectionMatrix * modelViewMatrix * vec4(_transformed, 1.0); gl_Position = projectionMatrix * modelViewMatrix * vec4(_transformed, 1.0);
` `
); );

View file

@ -1,4 +1,4 @@
import { Group, Mesh, MeshStandardMaterial, Scene } from "three"; import { Group, Material, Mesh, MeshStandardMaterial, Scene } from "three";
import { buildMeshes } from "./utils/build-meshes"; import { buildMeshes } from "./utils/build-meshes";
import { Extent, buildScene } from "./utils/build-scene"; import { Extent, buildScene } from "./utils/build-scene";
import { getCenter3D, getMetadata, transform } from "./utils/utils"; import { getCenter3D, getMetadata, transform } from "./utils/utils";
@ -12,6 +12,7 @@ import { DragControls } from "three/examples/jsm/Addons.js";
import { import {
DebugProvider, DebugProvider,
HeightDebugProvider, HeightDebugProvider,
MapHeightNode,
MapTilerProvider, MapTilerProvider,
MapView, MapView,
OpenStreetMapsProvider, OpenStreetMapsProvider,
@ -61,6 +62,20 @@ export class SceneView {
if (mesh) { if (mesh) {
mesh.visible = !mesh.visible; mesh.visible = !mesh.visible;
} }
// Set visibility for any existing cap meshes
for (const key of Object.values(Orientation)) {
const name = `cap-mesh-group-${key}`;
const capMeshGroup = this._scene.getObjectByName(name);
if (capMeshGroup) {
for (const m of capMeshGroup.children) {
if (m.name === layerName && m) {
m.visible = !m.visible;
}
}
}
}
} }
toggleCoordinateGrid() { toggleCoordinateGrid() {
@ -166,9 +181,10 @@ async function init(container: HTMLElement, modelId = MODEL_ID) {
// Create the map view for OSM topography // Create the map view for OSM topography
const map = new MapView(MapView.PLANAR, provider, heightProvider); const map = new MapView(MapView.PLANAR, provider, heightProvider);
const customNode = new CustomMapHeightNodeShader(null, map); const customNode = new CustomMapHeightNodeShader(undefined, map);
map.setRoot(customNode); map.setRoot(customNode);
map.rotateX(Math.PI / 2); map.rotateX(Math.PI / 2);
map.name = "topography"; map.name = "topography";
scene.add(map); scene.add(map);

View file

@ -470,7 +470,7 @@ function generateCapMeshes(
// Iterate over the list of geologic meshes // Iterate over the list of geologic meshes
for (const mesh of meshes) { for (const mesh of meshes) {
// Slice visible meshes only // Slice visible meshes only
if (mesh.visible) {
const position = mesh.geometry.attributes.position.array; const position = mesh.geometry.attributes.position.array;
const indices = mesh.geometry.index ? mesh.geometry.index.array : null; const indices = mesh.geometry.index ? mesh.geometry.index.array : null;
const edges: Array<[Vector3, Vector3]> = []; const edges: Array<[Vector3, Vector3]> = [];
@ -484,21 +484,9 @@ function generateCapMeshes(
const i2 = indices ? indices[i + 1] * 3 : (i + 1) * 3; const i2 = indices ? indices[i + 1] * 3 : (i + 1) * 3;
const i3 = indices ? indices[i + 2] * 3 : (i + 2) * 3; const i3 = indices ? indices[i + 2] * 3 : (i + 2) * 3;
const v1 = new Vector3( const v1 = new Vector3(position[i1], position[i1 + 1], position[i1 + 2]);
position[i1], const v2 = new Vector3(position[i2], position[i2 + 1], position[i2 + 2]);
position[i1 + 1], const v3 = new Vector3(position[i3], position[i3 + 1], position[i3 + 2]);
position[i1 + 2]
);
const v2 = new Vector3(
position[i2],
position[i2 + 1],
position[i2 + 2]
);
const v3 = new Vector3(
position[i3],
position[i3 + 1],
position[i3 + 2]
);
// Check if the triangle is cut by the plane // Check if the triangle is cut by the plane
const d1 = plane.distanceToPoint(v1); const d1 = plane.distanceToPoint(v1);
@ -521,14 +509,15 @@ function generateCapMeshes(
const polygons: Vector3[][] = buildPolygons(edges); const polygons: Vector3[][] = buildPolygons(edges);
// Clip cap surfaces with clipping planes // Clip cap surfaces with clipping planes
const clippingPlanes = planes.filter( const clippingPlanes = planes.filter((p) => !p.normal.equals(plane.normal));
(p) => !p.normal.equals(plane.normal)
);
const offset = orientation === Orientation.Z ? 1 : -1; const offset = orientation === Orientation.Z ? 1 : -1;
const material = new MeshStandardMaterial({ const material = new MeshStandardMaterial({
color: (mesh.material as MeshStandardMaterial).color, color: (mesh.material as MeshStandardMaterial).color,
side: DoubleSide, side: DoubleSide,
metalness: 0.0,
roughness: 0.75,
flatShading: true,
polygonOffset: true, polygonOffset: true,
polygonOffsetFactor: offset, polygonOffsetFactor: offset,
polygonOffsetUnits: offset, polygonOffsetUnits: offset,
@ -540,6 +529,8 @@ function generateCapMeshes(
const geometry = triangulatePolygon(polygon, plane); const geometry = triangulatePolygon(polygon, plane);
const capMesh = new Mesh(geometry, material); const capMesh = new Mesh(geometry, material);
capMesh.visible = mesh.visible;
capMesh.name = mesh.name;
// Offset mesh to avoid flickering // Offset mesh to avoid flickering
const normal = plane.normal.clone().multiplyScalar(offset); const normal = plane.normal.clone().multiplyScalar(offset);
@ -558,7 +549,6 @@ function generateCapMeshes(
capMeshes.push(...localMeshes); capMeshes.push(...localMeshes);
} }
}
return capMeshes; return capMeshes;
} }

View file

@ -7,6 +7,7 @@ import {
Group, Group,
Object3D, Object3D,
Vector3, Vector3,
HemisphereLight,
} from "three"; } from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { OrbitControls } from "three/addons/controls/OrbitControls.js";
@ -62,7 +63,8 @@ export function buildScene(container: HTMLElement, extent: Extent) {
controls = new OrbitControls(camera, renderer.domElement); controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(center.x, center.y, center.z); // Focus on the center controls.target.set(center.x, center.y, center.z); // Focus on the center
controls.enableDamping = true; // Smooth camera movement controls.enableDamping = true; // Smooth camera movement
controls.maxDistance = maxSize * 5; controls.maxDistance = maxSize * 3;
controls.minDistance = maxSize / 5;
controls.update(); controls.update();
// Scene // Scene
@ -97,17 +99,24 @@ function animate() {
function buildDefaultLights(scene: Scene, extent: Extent) { function buildDefaultLights(scene: Scene, extent: Extent) {
const center = getCenter3D(extent); const center = getCenter3D(extent);
const lightPosition = {
x: center.x,
y: center.y - 200000,
z: extent.zmax + 100000,
};
const lights = []; const lights = [];
// Ambient light // Ambient light
const ambient = new AmbientLight(0xffffff, 1.0); const ambient = new AmbientLight(0xffffff, 2);
lights.push(ambient); lights.push(ambient);
// Directional lights // Directional lights
const directionalLight = new DirectionalLight(0xffffff, 1); const directionalLight = new DirectionalLight(0xffffff, 2);
directionalLight.position.set( directionalLight.position.set(
center.x, lightPosition.x,
center.y - 200000, lightPosition.y,
extent.zmax + 100000 lightPosition.z
); );
// Create a target for the ligth // Create a target for the ligth