diff --git a/app/components/Form.tsx b/app/components/Form.tsx
index f1fd145..ac354df 100644
--- a/app/components/Form.tsx
+++ b/app/components/Form.tsx
@@ -259,9 +259,9 @@ export function Form() {
{sceneView?.model.children.map((child) => {
const key = `toggle-visibility-${child.name}`;
- const color = `#${(
- (child as Mesh).material as MeshStandardMaterial
- ).color.getHexString()}`;
+ //const color = `#${(
+ // (child as Mesh).material as MeshStandardMaterial
+ //).color.getHexString()}`;
const visible = (child as Mesh).visible;
return (
@@ -272,7 +272,7 @@ export function Form() {
{
- // Pass uniforms from userData to the
+ // Pass uniforms from userData
for (const i in material.userData) {
shader.uniforms[i] = material.userData[i];
}
diff --git a/app/three/SceneView.ts b/app/three/SceneView.ts
index 5d262c4..8cf6c9e 100644
--- a/app/three/SceneView.ts
+++ b/app/three/SceneView.ts
@@ -3,7 +3,9 @@ import {
Material,
Mesh,
MeshBasicMaterial,
+ MeshPhongMaterial,
MeshStandardMaterial,
+ Object3D,
PerspectiveCamera,
Plane,
Raycaster,
@@ -32,12 +34,16 @@ import {
} from "three/examples/jsm/Addons.js";
import {
LODFrustum,
+ LODRaycast,
+ MapPlaneNode,
MapTilerProvider,
MapView,
OpenStreetMapsProvider,
+ UnitsUtils,
} from "geo-three";
import { CustomMapHeightNodeShader } from "./CustomMapHeightNodeShader";
import { Data, createSVG } from "./utils/create-borehole-svg";
+import { TileData, updateTiles } from "./ShaderMaterial";
export type CustomEvent = CustomEventInit<{
element: SVGSVGElement | null;
@@ -489,18 +495,55 @@ async function init(container: HTMLElement, modelId = MODEL_ID) {
);
// Create the map view for OSM topography
- const lod = new LODFrustum();
+ const lod = new LODRaycast();
- // @ts-expect-error Type definition for MapView is incorrect - missing parameter for lod
- const map = new MapView(MapView.PLANAR, provider, heightProvider, lod);
- const customNode = new CustomMapHeightNodeShader(undefined, map);
- map.setRoot(customNode);
+ const map = new MapView(MapView.PLANAR, provider);
+ map.lod = lod;
+ // const customNode = new CustomMapHeightNodeShader(undefined, map);
+ // map.setRoot(customNode);
map.rotateX(Math.PI / 2);
map.name = "topography";
map.visible = false;
scene.add(map);
+ controls.addEventListener("change", () => {
+ const tiles: TileData[] = [];
+ function traverse(node: MapPlaneNode) {
+ if (node.isMesh) {
+ const bounds = UnitsUtils.tileBounds(node.level, node.x, node.y);
+
+ const xmin = bounds[0];
+ const ymin = bounds[2];
+ const xmax = xmin + bounds[1];
+ const ymax = ymin + bounds[3];
+
+ if (
+ (extent.xmax >= xmin && extent.ymax >= ymin) ||
+ (extent.xmin <= xmax && extent.ymax >= ymin) ||
+ (extent.xmin <= xmax && extent.ymin <= ymax) ||
+ (extent.xmax >= xmin && extent.ymin <= ymax)
+ ) {
+ tiles.push({
+ xmin,
+ ymin,
+ xmax,
+ ymax,
+ texture: (node.material as MeshPhongMaterial).map,
+ });
+ }
+ }
+ for (const c of node.children) {
+ traverse(c as MapPlaneNode);
+ }
+ }
+
+ map.lod.updateLOD(map, camera, renderer, scene);
+ traverse(map.root);
+
+ updateTiles(tiles.reverse());
+ });
+
return {
scene,
model,
diff --git a/app/three/ShaderMaterial.ts b/app/three/ShaderMaterial.ts
new file mode 100644
index 0000000..18ba003
--- /dev/null
+++ b/app/three/ShaderMaterial.ts
@@ -0,0 +1,98 @@
+import { ShaderMaterial, Texture, Vector4 } from "three";
+
+export interface TileData {
+ xmin: number;
+ ymin: number;
+ xmax: number;
+ ymax: number;
+ texture: Texture | null;
+}
+
+const maxTiles = 16;
+
+// Initialize empty texture slots
+const dummyTexture = new Texture();
+dummyTexture.image = document.createElement("canvas");
+dummyTexture.needsUpdate = true;
+
+// Create shader material
+export const shaderMaterial = new ShaderMaterial({
+ uniforms: {
+ tileTextures: { value: Array(maxTiles).fill(dummyTexture) },
+ tileBounds: { value: Array(maxTiles).fill(new Vector4(0, 0, 0, 0)) },
+ tileCount: { value: 0 },
+ },
+ vertexShader: `
+ varying vec3 vWorldPosition;
+ void main() {
+ vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
+ gl_Position = projectionMatrix * viewMatrix * vec4(vWorldPosition, 1.0);
+ }
+ `,
+ fragmentShader: `
+ uniform sampler2D tileTextures[${maxTiles}];
+ uniform vec4 tileBounds[${maxTiles}];
+ uniform int tileCount;
+ varying vec3 vWorldPosition;
+
+ void main() {
+ vec4 color = vec4(1.0, 1.0, 1.0, 1.0); // Default color
+
+ for (int i = 0; i < ${maxTiles}; i++) {
+ if (i >= tileCount) break; // Only process available tiles
+
+ vec4 bounds = tileBounds[i];
+
+ if (vWorldPosition.x >= bounds.x && vWorldPosition.x <= bounds.y &&
+ vWorldPosition.y >= bounds.z && vWorldPosition.y <= bounds.w) {
+
+ vec2 uv = (vWorldPosition.xy - bounds.xz) / (bounds.yw - bounds.xz);
+ switch (i) {
+ case 0: color = texture2D(tileTextures[0], uv); break;
+ case 1: color = texture2D(tileTextures[1], uv); break;
+ case 2: color = texture2D(tileTextures[2], uv); break;
+ case 3: color = texture2D(tileTextures[3], uv); break;
+ case 4: color = texture2D(tileTextures[4], uv); break;
+ case 5: color = texture2D(tileTextures[5], uv); break;
+ case 6: color = texture2D(tileTextures[6], uv); break;
+ case 7: color = texture2D(tileTextures[7], uv); break;
+ case 8: color = texture2D(tileTextures[8], uv); break;
+ case 9: color = texture2D(tileTextures[9], uv); break;
+ case 10: color = texture2D(tileTextures[10], uv); break;
+ case 11: color = texture2D(tileTextures[11], uv); break;
+ case 12: color = texture2D(tileTextures[12], uv); break;
+ case 13: color = texture2D(tileTextures[13], uv); break;
+ case 14: color = texture2D(tileTextures[14], uv); break;
+ case 15: color = texture2D(tileTextures[15], uv); break;
+ }
+
+ break; // Stop checking once we find the correct tile
+ }
+ }
+
+ gl_FragColor = color;
+ }
+ `,
+});
+
+export function updateTiles(newTiles: TileData[]) {
+ if (newTiles.length > maxTiles) {
+ newTiles = newTiles.slice(0, maxTiles);
+ }
+
+ const textures = newTiles.map((t) => t.texture);
+ const bounds = newTiles.map(
+ (t) => new Vector4(t.xmin, t.xmax, t.ymin, t.ymax)
+ );
+
+ // Fill remaining slots with dummy data to maintain uniform array size
+ while (textures.length < maxTiles) {
+ textures.push(dummyTexture);
+ bounds.push(new Vector4(0, 0, 0, 0));
+ }
+
+ // Update shader uniforms
+ shaderMaterial.uniforms.tileTextures.value = textures;
+ shaderMaterial.uniforms.tileBounds.value = bounds;
+ shaderMaterial.uniforms.tileCount.value = newTiles.length;
+}
diff --git a/app/three/utils/build-meshes.ts b/app/three/utils/build-meshes.ts
index 26ec0a4..7fddd1d 100644
--- a/app/three/utils/build-meshes.ts
+++ b/app/three/utils/build-meshes.ts
@@ -61,7 +61,7 @@ async function buildMesh(layerData: MappedFeature) {
const material = new MeshStandardMaterial({
color: color,
metalness: 0.0,
- roughness: 1.0,
+ roughness: 5.0,
flatShading: true,
side: DoubleSide,
wireframe: false,