Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9314d7c
feat(demo): add demo section with multiple scenes and transitions
Nynjin Nov 14, 2025
eb8bd84
feat(demo): sceneManager with navigation + added tilt to scenes
Nynjin Nov 14, 2025
b12b2c9
fix(demo): render the same scene twice + scene heading
Nynjin Nov 14, 2025
51b9290
feat(demo): added transition between 3D View and Immersive View + man…
Nynjin Nov 14, 2025
75f247d
chore(demo): typescript migration
Nynjin Nov 14, 2025
4b7981d
feat(demo): added scene ready state + allow scene to have different c…
Nynjin Nov 14, 2025
ddb438d
fix(demo): prevent camera controls during scene transition
Nynjin Nov 14, 2025
0db51c3
feat(demo): updated 2D projected data and 3D extruded data scenes to …
Nynjin Nov 14, 2025
9b78981
feat(demo): added InstancedData scene with trees in Lyon using GLTF l…
Nynjin Nov 14, 2025
fedda18
feat(demo): PointCloud scene representing Part Dieu station
Nynjin Nov 14, 2025
17f3afe
feat(demo): added intermediate view for cleaner camera movements betw…
Nynjin Nov 14, 2025
f8ee80d
feat(demo): added scene title and description + reset scene and reset…
Nynjin Nov 14, 2025
48fefaa
fix(demo): UI updates and navigation buttons
Nynjin Nov 14, 2025
1933941
fix(demo): smoother camera transitions
Nynjin Nov 14, 2025
96f39e6
fix(demo): Point Cloud scene
Nynjin Nov 17, 2025
e783568
fix(demo): pointCloudLayer remaining visible after transition
Nynjin Nov 17, 2025
125cf73
fix(demo): duplicate layers adding to view
Nynjin Nov 18, 2025
f6cacfc
feat(demo): added tooltips for park layer and config GUI to scene
Nynjin Nov 19, 2025
c2be1d1
feat(demo): add 3D textured meshes scene
Nynjin Nov 19, 2025
c7885a5
feat(demo): added planar view of lyon
Nynjin Nov 19, 2025
09cd567
fix(demo): navigation buttons fixed position
Nynjin Nov 19, 2025
5a15e7f
fix(demo): Layer typing
Nynjin Nov 20, 2025
9a357e2
perf(demo): scene transition layer management
Nynjin Nov 20, 2025
a00b7ea
feat(demo): BIM visualization scene
Nynjin Nov 21, 2025
b40c0ea
feat(demo): compatibility with three-geospatial lighting and atmosphere
Nynjin Nov 24, 2025
ecfd787
revert(demo): remove debug UI and bugged featureInfo
Nynjin Nov 24, 2025
d349f97
feat(demo): improve soft reset and added hard reset for scene
Nynjin Nov 25, 2025
8386b34
feat(demo): atmosphere added/removed automatically on distance from g…
Nynjin Nov 25, 2025
1e5ee26
feat(demo): replaced forward/backward navigation with one button per …
Nynjin Nov 25, 2025
e9b1381
fix(demo): pointer events blocked by demo ui
Nynjin Nov 25, 2025
c76af3f
fix(demo): scene reset
Nynjin Nov 25, 2025
af59113
feat(demo): better text contrast
Nynjin Nov 25, 2025
160b2c1
fix(demo): layer dissapearing on scene transition
Nynjin Nov 26, 2025
89f4b2a
feat(demo): update scene titles and descriptions + scene order
Nynjin Nov 26, 2025
436a0c7
fix(demo): elevation layer artefacts
Nynjin Nov 26, 2025
9103806
fix(demo): error after ImmersiveView reset scene
Nynjin Nov 26, 2025
b6f5ff1
feat(demo): moved entire demo UI to center-bottom + extra zoom on Tex…
Nynjin Nov 27, 2025
64a80a3
feat(demo): added Feature Picker
Nynjin Nov 28, 2025
8b4c3de
fix(demo): View3d camera teleporting to scene range
Nynjin Nov 28, 2025
d33eb45
fix(demo): config.ts to Config.ts rename not registered by git
Nynjin Nov 28, 2025
2e08335
feat(demo): added Point Cloud controls
Nynjin Nov 28, 2025
0dae161
fix(demo): feature picker interaction remaining when layers invisible
Nynjin Nov 28, 2025
3f32e47
fix(demo): prevent events to propagate from UI elements to itowns view
Nynjin Dec 1, 2025
da68e42
fix(demo): GlobeView skymanager property not found by typescript
Nynjin Dec 2, 2025
70b2e3e
chore(demo): update demo deps, exports, and webpack config
Nynjin Dec 9, 2025
4a531e7
fix(demo): remove featurePicking UI when in a scene that doesn't use it
Nynjin Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"Demo": {
"demo/index": "demonstration"
},
"View": {
"view_3d_map": "3D map",
"view_25d_map": "2.5D map",
Expand Down
21 changes: 21 additions & 0 deletions examples/demo/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"presets": [
["@babel/preset-typescript"],
["@babel/preset-env", {
"targets": {
"browsers": "defaults and supports webgl2"
},
"modules": false
}]
],
"plugins": [
["module-resolver", {
"cwd": "packagejson",
"root": ["./src"],
"extensions": [".js", ".ts", ".tsx"]
}],
["module-extension-resolver", {
"srcExtensions": [".ts", ".js"]
}],
]
}
3 changes: 3 additions & 0 deletions examples/demo/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist/
node_modules/
coverage/
11 changes: 11 additions & 0 deletions examples/demo/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

module.exports = {
extends: [
'../../.eslintrc.cjs',
],
rules: {
'import/extensions': 'off',
'no-constructor-return': 'off',
'no-return-assign': 'off',
},
};
167 changes: 167 additions & 0 deletions examples/demo/css/demo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#demoUI {
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 1vh;

display: flex;
flex-direction: column;
align-items: center;

width: min(80vw, 70vh);
max-height: 40vh;
padding: 2vh 3vh;
border-radius: 3rem;
background-color: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(0.5vh);
z-index: 100;
}

#textContainer {
position: static;

display: flex;
flex-direction: column;

color: black;
text-align: center;
font-family:'Courier New', Courier, monospace;
}

/*** Navigation Menu ***/
#navigationContainer {
position: static;

display: flex;
flex-direction: row;
justify-content: center;
align-items: center;

width: 100%;
gap: 2vh;
margin-top: 2vh;
}

button {
display: inline-block;

border: 1px solid black;
border-radius: 0.5rem;
padding: 0.5vh 2vh;

color: black;
text-align: center;
text-decoration: none;
font-size: min(1vh, 1.5vw);
font-family:'Courier New', Courier, monospace;

cursor: pointer;
}

button:hover {
background-color: #E5E5E5;
color: black;
}

button:active {
background-color: #7EA8C5;
color: white;
}

button.active {
background-color: #7EA8C5;
color: white;
}

#sceneDots {
display: flex;
flex-direction: row;
gap: min(1vh, 1.5vw);
align-items: center;
}

.scene-dot {
width: min(1.5vh, 2vw);
height: min(1.5vh, 2vw);
border-radius: 50%;
border: 1px solid #000;
background-color: #ffffff;
cursor: pointer;
transition: transform 0.15s ease, background-color 0.15s ease;
}

.scene-dot.active {
background-color: #7EA8C5;
transform: scale(1.4);
}

.scene-dot:hover {
background-color: #E5E5E5;
}

/*** Feature Picking Info ***/
#feature-picking-info {
position: fixed;
top: 1vh;
right: 1vh;
padding: 1vh 1.5vh;
border-radius: 1vh;
overflow-y: auto;

font-family:'Courier New', Courier, monospace;
font-size: min(1.5vh, 2vw);
color: black;

width: 25vw;
max-height: 45vh;
background-color: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(0.5vh);

z-index: 100;

pointer-events: auto;
}

/*** Point Cloud Config ***/
#point-cloud-config {
position: fixed;
top: 1vh;
right: 1vh;
padding: 1vh 1.5vh;
border-radius: 1vh;

display: flex;
flex-direction: column;
justify-content: center;

font-family:'Courier New', Courier, monospace;
font-size: min(1.5vh, 2vw);
color: black;

width: 25vw;
max-height: 45vh;
background-color: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(0.5vh);

z-index: 100;
}

#point-cloud-buttons {
width: 100%;
display: flex;
justify-self: center;
}

#point-cloud-config button {
width: 50%;
}

#point-cloud-config button:first-child {
border-right: none;
border-radius: 0.5rem 0 0 0.5rem;
}

#point-cloud-config button:last-child {
border-left: none;
border-radius: 0 0.5rem 0.5rem 0;
}
162 changes: 162 additions & 0 deletions examples/demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!DOCTYPE html>
<html>
<head>
<title>Itowns - Demonstration</title>

<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<script type="importmap">
{
"imports": {
"itowns": "../../dist/itowns.js",
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/",
"OrbitControls": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js"
}
}
</script>

<link rel="stylesheet" type="text/css" href="../css/example.css" />
<link
rel="stylesheet"
type="text/css"
href="../css/LoadingScreen.css"
/>
<link rel="stylesheet" type="text/css" href="../css/widgets.css" />
<link rel="stylesheet" type="text/css" href="css/demo.css" />
</head>
<body>
<div id="demoUI">
<div id="textContainer">
<h1 id="sceneTitle"></h1>
<p id="sceneDescription"></p>
</div>
<div id="navigationContainer">
<div id="sceneDots"></div>
<button id="resetScene">reset camera</button>
<button id="hardResetScene">reset scene</button>
</div>
</div>
<script type="module">
import * as THREE from "three";
import * as itowns from "itowns";

let SceneTransitionUtils, SceneRepository;

try {
// If webpack served, import from global build
const mod1 = await import("../../dist/demo.js");
SceneTransitionUtils = mod1.SceneTransitionUtils;
SceneRepository = mod1.SceneRepository;
} catch (e) {
// Otherwise, import from local build
const mod2 = await import("./dist/index.js");
SceneTransitionUtils = mod2.SceneTransitionUtils;
SceneRepository = mod2.SceneRepository;
}

const resetSceneButton = document.getElementById("resetScene");
const hardResetSceneButton = document.getElementById("hardResetScene");

SceneTransitionUtils.updateUIForScene(SceneRepository[0]);
SceneRepository[0].view.setVisible(true);
await SceneRepository[0].onCreate();
await SceneRepository[0].onEnter?.();
const view = SceneRepository[0].view.getView();
if (view instanceof itowns.GlobeView) {
view.skyManager.enabled = SceneRepository[0].atmosphere;
}
await SceneTransitionUtils.moveCameraTo(view, SceneRepository[0].placement, 0.1);

let currentIndex = 0;

const SceneManager = {
async goTo(index) {
if (index === currentIndex || index < 0 || index >= SceneRepository.length) {
return;
}
const from = SceneRepository[currentIndex];
const to = SceneRepository[index];
currentIndex = index;
updateActiveDot();

toggleEnableButtons(false);
toggleEnableDots(false);
try {
await SceneTransitionUtils.transitionToScene(from, to);
} finally {
toggleEnableButtons(true);
toggleEnableDots(true);
}
},
async softReset() {
toggleEnableButtons(false);
toggleEnableDots(false);
try {
await SceneTransitionUtils.resetScene(SceneRepository[currentIndex]);
} finally {
toggleEnableButtons(true);
toggleEnableDots(true);
}
},
async hardReset() {
toggleEnableButtons(false);
toggleEnableDots(false);
try {
await SceneTransitionUtils.hardResetScene(SceneRepository[currentIndex]);
} finally {
toggleEnableButtons(true);
toggleEnableDots(true);
}
},
};

// ---- Build the dots UI ----
const dotsContainer = document.getElementById("sceneDots");

const dots = SceneRepository.map((scene, index) => {
const dot = document.createElement("div");
dot.classList.add("scene-dot");
dot.title = scene.title ?? `Scene ${index + 1}`;
dot.addEventListener("click", () => {
SceneManager.goTo(index);
});
dotsContainer.appendChild(dot);
return dot;
});

function toggleEnableButtons(enable) {
resetSceneButton.style.pointerEvents = enable ? "auto" : "none";
hardResetSceneButton.style.pointerEvents = enable ? "auto" : "none";
resetSceneButton.disabled = !enable;
hardResetSceneButton.disabled = !enable;
}

function toggleEnableDots(enable) {
dots.forEach((dot) => {
dot.style.pointerEvents = enable ? "auto" : "none";
dot.style.opacity = enable ? "1.0" : "0.5";
});
}

function updateActiveDot() {
dots.forEach((dot, i) => {
if (i === currentIndex) {
dot.classList.add("active");
} else {
dot.classList.remove("active");
}
});
}

updateActiveDot();
resetSceneButton.onclick = async () => {
await SceneManager.softReset();
};
hardResetSceneButton.onclick = async () => {
await SceneManager.hardReset();
};
</script>
</body>
</html>
Loading