-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
270 lines (241 loc) · 11.4 KB
/
main.js
File metadata and controls
270 lines (241 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
// Main entry point - wires together all modules
import { createViewer } from './src/core/viewer.js';
import { loadModels, hideLoading, showError } from './src/load/loadModels.js';
import { buildTowerInstancedMeshes, processRegularModel } from './src/instancing/towerInstancer.js';
import { initLidarBoard } from './src/ui/lidarBoard.js';
import { createVideoOverlay } from './src/overlay/videoOverlay.js';
import { createSync } from './src/sync/controller.js';
import { createModelFocus } from './src/focus/modelFocus.js';
import models from './src/config/models.js';
// Wait for DOM to be ready
document.addEventListener('DOMContentLoaded', async function() {
console.log('Starting modular architectural system...');
console.log('Modules: viewer, loader, instancer, lidarBoard, sync, modelFocus');
// MOBILE FIX: Force loading screen dismissal after max timeout
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const maxLoadingTime = isMobile ? 5000 : 10000; // 5s on mobile, 10s on desktop
const loadingFallbackTimer = setTimeout(() => {
console.warn('Loading timeout reached, forcing dismissal');
const loadingElement = document.getElementById('loading');
if (loadingElement && loadingElement.style.display !== 'none') {
hideLoading(loadingElement);
}
}, maxLoadingTime);
// Get DOM elements
const canvas = document.getElementById('canvas');
const loadingElement = document.getElementById('loading');
const lidarBoardElement = document.getElementById('lidar-board');
const highlightBtn = document.getElementById('btnHighlight');
const zoomExtentsBtn = document.getElementById('btnZoomExtents');
// Initialize 3D viewer
const viewer = createViewer(canvas);
const { scene, render, dispose } = viewer;
// Initialize sync controller (placeholder callbacks for now)
const sync = createSync({ viewer, lidar: null }); // Will connect lidar after init
// Initialize model focus system - MUST be after scene is created
let modelFocus = null; // Will be initialized after models are loaded
// Initialize video overlay system with model focus integration
const videoOverlay = createVideoOverlay(lidarBoardElement, {
onOverlayOpen: (region, hotspot) => {
console.log(`Video overlay opened for: ${region}`);
// Model focus is already applied when hotspot was clicked
// Disable highlighting when overlay opens
if (lidar.getHighlighting()) {
lidar.setHighlighting(false);
}
// Hide the Highlight button while zoomed/overlayed
highlightBtn.style.display = 'none';
},
onOverlayClose: () => {
console.log('Video overlay closed');
// Clear model focus when overlay closes
if (modelFocus) {
modelFocus.clearFocus();
console.log('Cleared 3D model focus - all models restored');
// Force immediate render to show focus clear
render();
}
// Overlay closed (via × or ESC) → show Highlight button again
highlightBtn.style.display = 'block';
// Force lidar board to reset state after overlay closes
setTimeout(() => {
if (lidar.resetState) {
lidar.resetState();
}
}, 100);
}
});
// Initialize LiDAR board with video overlay integration and color hover effects
const lidar = initLidarBoard(lidarBoardElement, {
onSelect: (area, hotspot) => {
// Apply model focus IMMEDIATELY when hotspot is clicked (before zoom/overlay)
if (modelFocus) {
modelFocus.focusOnRegion(area);
console.log(`Applied 3D model focus immediately for region: ${area}`);
// Force immediate render to show focus effect
render();
}
// Show video overlay (this will trigger zoom animation)
videoOverlay.showOverlay(area, hotspot);
// Still sync for any 3D interactions
sync.handleAreaSelect(area, hotspot);
},
onHover: (area, hotspot) => {
// Apply subtle color tinting on hover
console.log(`HOVER DEBUG: Attempting to apply color for region: ${area}`);
if (modelFocus) {
try {
modelFocus.applyHoverHighlight(area);
console.log(`✓ Applied hover color highlight for region: ${area}`);
// Force immediate render to show color changes
render();
} catch (error) {
console.error('Error applying hover highlight:', error);
}
} else {
console.warn('modelFocus not available for hover highlighting');
}
},
onHoverEnd: (area, hotspot) => {
// Remove color tinting when hover ends (but only if not in focus mode)
if (modelFocus && !modelFocus.isFocused()) {
modelFocus.removeHoverHighlight();
console.log(`Removed hover color highlight for region: ${area}`);
// Force immediate render to show color changes
render();
}
},
onZoomExtents: () => {
// Clear model focus FIRST when zoom extents is clicked
if (modelFocus) {
modelFocus.clearFocus();
console.log('Cleared 3D model focus via Zoom Extents');
// Force immediate render to show focus clear
render();
}
// Use video overlay's zoom toggle for proper state management
videoOverlay.toggleZoom();
// Only after zoom reset, re-enable the Highlight button
highlightBtn.style.display = 'block';
// Force clean state reset after zoom extents
setTimeout(() => {
if (lidar.resetState) {
lidar.resetState();
}
}, 900); // After zoom animation completes
// Still sync for any 3D interactions
sync.handleZoomExtents();
}
});
// Add question mark hover effects to hotspots
const hotspots = lidarBoardElement.querySelectorAll('.hotspot');
videoOverlay.addHoverEffects(hotspots);
try {
// Load all models
const loadedScenes = await loadModels(models, {
onProgress: (loaded, total, modelResult) => {
console.log(`Model loading progress: ${loaded}/${total} - ${modelResult.name}`);
},
onLoaded: (scenes) => {
console.log('All models loaded, building scene...');
},
onError: (error, modelInfo) => {
showError(loadingElement, `Failed to load ${modelInfo.name}: ${error.message}`);
}
});
// Process loaded models
loadedScenes.forEach(modelResult => {
const { name, scene: modelScene, isInstanced } = modelResult;
if (isInstanced) {
// Build instanced tower system
console.log(`Building instanced system for: ${name}`);
buildTowerInstancedMeshes(scene, modelScene);
} else {
// Process and add regular models
console.log(`Adding regular model: ${name}`);
processRegularModel(modelScene, name);
scene.add(modelScene);
}
});
// Initialize model focus system AFTER all models are loaded and added to scene
modelFocus = createModelFocus(scene);
console.log('Model focus system initialized with all models cataloged');
// Catalog all models for the focus system with proper name mapping
loadedScenes.forEach(modelResult => {
const { name, scene: modelScene } = modelResult;
// Map file names to display names for focus system
const displayName = mapModelName(name);
if (modelFocus && modelFocus.catalogNewModel) {
modelFocus.catalogNewModel(modelScene, displayName);
}
});
// Hide loading screen
clearTimeout(loadingFallbackTimer); // Clear the fallback timer
hideLoading(loadingElement);
// Show onboarding overlay after loading
setTimeout(() => {
const onboarding = document.getElementById('onboarding-overlay');
if (onboarding && !sessionStorage.getItem('onboardingShown')) {
onboarding.classList.add('active');
setTimeout(() => {
onboarding.classList.add('visible');
}, 50);
// Set up close handlers
const closeBtn = onboarding.querySelector('.onboarding-close');
const startBtn = onboarding.querySelector('.onboarding-start');
const closeOnboarding = () => {
onboarding.classList.remove('visible');
setTimeout(() => {
onboarding.classList.remove('active');
sessionStorage.setItem('onboardingShown', 'true');
}, 400);
};
closeBtn.addEventListener('click', closeOnboarding);
startBtn.addEventListener('click', closeOnboarding);
// Close on Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && onboarding.classList.contains('visible')) {
closeOnboarding();
}
});
}
}, 500);
// Initial render
render();
console.log('Modular system initialization complete with model focus integration!');
console.log('Available focus regions:', modelFocus ? modelFocus.getAvailableRegions() : 'None');
// Expose modelFocus for debugging (development only)
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
window.modelFocus = modelFocus;
console.log('Model focus system exposed to window.modelFocus for debugging');
}
} catch (error) {
console.error('Failed to initialize system:', error);
showError(loadingElement, 'System initialization failed: ' + error.message);
}
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
console.log('Cleaning up modular system...');
dispose();
});
});
/**
* Map model file names to display names used by focus system
* @param {string} fileName - Original file name from models config
* @returns {string} Display name for focus system
*/
function mapModelName(fileName) {
const nameMapping = {
'Architectural System': 'Architectural System',
'Misc Geometry': 'Misc Geometry',
'Altars': 'Altars',
'Circulation': 'Circulation',
'Distress': 'Distress',
'Embellishments': 'Embellishments',
'Index': 'Index',
'Mirror': 'Mirror',
'Moulage': 'Moulage',
'Robot': 'Robot'
};
return nameMapping[fileName] || fileName;
}