@littlecarlito/blorktools 0.50.4 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +69 -0
- package/package.json +13 -7
- package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
- package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
- package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
- package/src/asset_debugger/header/header.css +73 -0
- package/src/asset_debugger/header/header.html +24 -0
- package/src/asset_debugger/header/header.js +224 -0
- package/src/asset_debugger/index.html +76 -0
- package/src/asset_debugger/landing-page/landing-page.css +396 -0
- package/src/asset_debugger/landing-page/landing-page.html +81 -0
- package/src/asset_debugger/landing-page/landing-page.js +611 -0
- package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
- package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
- package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
- package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
- package/src/asset_debugger/main.css +14 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
- package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
- package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
- package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
- package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
- package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
- package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
- package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
- package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
- package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
- package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
- package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
- package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
- package/src/asset_debugger/router.js +190 -0
- package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
- package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
- package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
- package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
- package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
- package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
- package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
- package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
- package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
- package/src/asset_debugger/util/common.css +280 -0
- package/src/asset_debugger/util/data/animation-classifier.js +323 -0
- package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
- package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
- package/src/asset_debugger/util/data/glb-classifier.js +290 -0
- package/src/asset_debugger/util/data/html-formatter.js +76 -0
- package/src/asset_debugger/util/data/html-linter.js +276 -0
- package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
- package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
- package/src/asset_debugger/util/data/string-serder.js +303 -0
- package/src/asset_debugger/util/data/texture-classifier.js +663 -0
- package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
- package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
- package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
- package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
- package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
- package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
- package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
- package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
- package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
- package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
- package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
- package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
- package/src/asset_debugger/util/rig/rig-controller.js +612 -0
- package/src/asset_debugger/util/rig/rig-factory.js +628 -0
- package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
- package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
- package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
- package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
- package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
- package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
- package/src/asset_debugger/util/scene/background-manager.js +284 -0
- package/src/asset_debugger/util/scene/camera-controller.js +243 -0
- package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
- package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
- package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
- package/src/asset_debugger/util/scene/glb-controller.js +208 -0
- package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
- package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
- package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
- package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
- package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
- package/src/asset_debugger/util/scene/ui-manager.js +107 -0
- package/src/asset_debugger/util/state/animation-state.js +128 -0
- package/src/asset_debugger/util/state/css3d-state.js +83 -0
- package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
- package/src/asset_debugger/util/state/log-util.js +197 -0
- package/src/asset_debugger/util/state/scene-state.js +452 -0
- package/src/asset_debugger/util/state/threejs-state.js +54 -0
- package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
- package/src/asset_debugger/util/workers/model-worker.js +109 -0
- package/src/asset_debugger/util/workers/texture-worker.js +54 -0
- package/src/asset_debugger/util/workers/worker-manager.js +212 -0
- package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
- package/src/index.html +261 -0
- package/src/index.js +8 -0
- package/vite.config.js +66 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatHtml as externalFormatHtml,
|
|
3
|
+
initHtmlFormatter
|
|
4
|
+
} from '../../util/data/html-formatter.js';
|
|
5
|
+
import {
|
|
6
|
+
initHtmlLinter,
|
|
7
|
+
lintHtmlContent
|
|
8
|
+
} from '../../util/data/html-linter.js';
|
|
9
|
+
import {
|
|
10
|
+
MESH_BINARY_EXTENSION,
|
|
11
|
+
MESH_INDEX_PROPERTY,
|
|
12
|
+
BINARY_DATA_PROPERTY
|
|
13
|
+
} from '../../util/state/glb-preview-state.js';
|
|
14
|
+
import * as THREE from 'three';
|
|
15
|
+
import { isPreviewActive, setLastTextureUpdateTime } from '../../util/state/animation-state';
|
|
16
|
+
import { initalizePreview } from '../../util/animation/playback/animation-preview-controller';
|
|
17
|
+
import { previewRenderTarget } from '../../util/state/threejs-state.js';
|
|
18
|
+
import {
|
|
19
|
+
getHtmlSettingsForMesh,
|
|
20
|
+
loadHtmlForMesh,
|
|
21
|
+
loadSettingsForMesh,
|
|
22
|
+
saveHtmlForMesh,
|
|
23
|
+
saveSettingsForMesh
|
|
24
|
+
} from '../../util/data/mesh-html-manager.js';
|
|
25
|
+
import { cleanupThreeJsPreview } from '../../util/scene/threejs-preview-manager';
|
|
26
|
+
import { updateHtmlIcons } from '../../panels/asset-panel/mesh-heading/mesh-heading.js';
|
|
27
|
+
|
|
28
|
+
let maxCaptureRate = 0.5;
|
|
29
|
+
|
|
30
|
+
export const defaultSettings = {
|
|
31
|
+
previewMode: 'threejs',
|
|
32
|
+
playbackSpeed: 1.0,
|
|
33
|
+
animation: {
|
|
34
|
+
type: 'play'
|
|
35
|
+
},
|
|
36
|
+
display: {
|
|
37
|
+
showBorders: true
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let listenersInitialized = false;
|
|
42
|
+
|
|
43
|
+
let htmlEditorState = {
|
|
44
|
+
isOpen: false,
|
|
45
|
+
changesSaved: false
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let lintDebounceTimer = null;
|
|
49
|
+
|
|
50
|
+
export class CustomTextureSettings {
|
|
51
|
+
constructor(html, meshId, previewMode, playbackSpeed, animationType, showPreviewBorders,
|
|
52
|
+
statusCallback = null, errorCallback = null, errorContainer = null) {
|
|
53
|
+
this.html = html;
|
|
54
|
+
this.meshId = meshId;
|
|
55
|
+
this.previewMode = previewMode;
|
|
56
|
+
this.playbackSpeed = playbackSpeed;
|
|
57
|
+
this.animationType = animationType;
|
|
58
|
+
this.showPreviewBorders = showPreviewBorders;
|
|
59
|
+
this.statusCallback = statusCallback;
|
|
60
|
+
this.errorCallback = errorCallback;
|
|
61
|
+
this.errorContainer = errorContainer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get isLongExposureMode() {
|
|
65
|
+
return this.animationType === 'longExposure';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
updateStatus(message, type = 'info') {
|
|
69
|
+
if (this.statusCallback) {
|
|
70
|
+
this.statusCallback(message, type);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
handleError(message) {
|
|
75
|
+
if (this.errorCallback) {
|
|
76
|
+
this.errorCallback(message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function openEmbeddedHtmlEditor(meshName, meshId) {
|
|
82
|
+
console.log(`openEmbeddedHtmlEditor called for mesh: ${meshName} (ID: ${meshId})`);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const modal = document.getElementById('html-editor-modal');
|
|
86
|
+
if (!modal) {
|
|
87
|
+
console.error('HTML Editor Modal element not found in the DOM');
|
|
88
|
+
alert('Error: Could not find HTML Editor Modal. Please try again.');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const meshNameEl = document.getElementById('html-editor-mesh-name');
|
|
93
|
+
const textarea = document.getElementById('html-editor-textarea');
|
|
94
|
+
const previewContainer = document.getElementById('html-preview-container');
|
|
95
|
+
const statusEl = document.getElementById('html-editor-status');
|
|
96
|
+
|
|
97
|
+
console.log('Found all required modal elements:', {
|
|
98
|
+
modal: !!modal,
|
|
99
|
+
meshNameEl: !!meshNameEl,
|
|
100
|
+
textarea: !!textarea,
|
|
101
|
+
previewContainer: !!previewContainer
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (meshNameEl) meshNameEl.textContent = meshName;
|
|
105
|
+
|
|
106
|
+
modal.dataset.meshId = meshId;
|
|
107
|
+
|
|
108
|
+
const forceReload = htmlEditorState.needsReload === true;
|
|
109
|
+
if (forceReload) {
|
|
110
|
+
console.log('Forcing reload from binary buffer for mesh ID:', meshId);
|
|
111
|
+
htmlEditorState.needsReload = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (forceReload) {
|
|
115
|
+
const meshDataUtil = await import('../../util/data/mesh-html-manager');
|
|
116
|
+
meshDataUtil.clearMeshHtmlSettings(meshId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
loadSettingsForMesh(meshId).then(settings => {
|
|
120
|
+
console.log('Loaded settings:', settings);
|
|
121
|
+
|
|
122
|
+
const renderTypeSelect = document.getElementById('html-render-type');
|
|
123
|
+
const playbackSpeedSelect = document.getElementById('html-playback-speed');
|
|
124
|
+
const animationTypeSelect = document.getElementById('html-animation-type');
|
|
125
|
+
const showWireframeCheckbox = document.getElementById('show-wireframe');
|
|
126
|
+
const displayOnMeshCheckbox = document.getElementById('display-on-mesh');
|
|
127
|
+
const rigControlNodeCheckbox = document.getElementById('rig-control-node');
|
|
128
|
+
const dropdownsContainer = document.getElementById('editor-dropdowns-container');
|
|
129
|
+
|
|
130
|
+
if (renderTypeSelect) {
|
|
131
|
+
if (settings.previewMode && ['threejs', 'css3d', 'longExposure'].includes(settings.previewMode)) {
|
|
132
|
+
renderTypeSelect.value = settings.previewMode;
|
|
133
|
+
} else {
|
|
134
|
+
renderTypeSelect.value = 'threejs';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (playbackSpeedSelect) {
|
|
139
|
+
const speedValue = settings.playbackSpeed ? settings.playbackSpeed.toString() : '1.0';
|
|
140
|
+
|
|
141
|
+
const speedExists = Array.from(playbackSpeedSelect.options).some(option => option.value === speedValue);
|
|
142
|
+
|
|
143
|
+
if (speedExists) {
|
|
144
|
+
playbackSpeedSelect.value = speedValue;
|
|
145
|
+
} else {
|
|
146
|
+
playbackSpeedSelect.value = '1.0';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (animationTypeSelect && settings.animation && settings.animation.type) {
|
|
151
|
+
if (['play', 'loop', 'bounce'].includes(settings.animation.type)) {
|
|
152
|
+
animationTypeSelect.value = settings.animation.type;
|
|
153
|
+
} else {
|
|
154
|
+
animationTypeSelect.value = 'play';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (showWireframeCheckbox && settings.display) {
|
|
159
|
+
showWireframeCheckbox.checked = settings.display.showBorders !== undefined ?
|
|
160
|
+
settings.display.showBorders : true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (displayOnMeshCheckbox && settings.display) {
|
|
164
|
+
displayOnMeshCheckbox.checked = settings.display.displayOnMesh || false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (rigControlNodeCheckbox && settings.display) {
|
|
168
|
+
rigControlNodeCheckbox.checked = settings.display.rigControlNode || false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (renderTypeSelect && renderTypeSelect.value === 'longExposure' && dropdownsContainer) {
|
|
172
|
+
dropdownsContainer.classList.add('long-exposure-mode');
|
|
173
|
+
} else if (dropdownsContainer) {
|
|
174
|
+
dropdownsContainer.classList.remove('long-exposure-mode');
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
loadHtmlForMesh(meshId, forceReload).then(html => {
|
|
179
|
+
if (textarea) textarea.value = html || '';
|
|
180
|
+
|
|
181
|
+
modal.classList.remove('preview-mode');
|
|
182
|
+
|
|
183
|
+
modal.classList.add('visible');
|
|
184
|
+
htmlEditorState.isOpen = true;
|
|
185
|
+
console.log('HTML Editor Modal opened successfully');
|
|
186
|
+
|
|
187
|
+
lintHtmlContent();
|
|
188
|
+
}).catch(error => {
|
|
189
|
+
console.error('Error loading HTML content:', error);
|
|
190
|
+
if (textarea) textarea.value = '';
|
|
191
|
+
if (statusEl) showStatus(`Error loading HTML: ${error.message}`, 'error');
|
|
192
|
+
|
|
193
|
+
modal.classList.remove('preview-mode');
|
|
194
|
+
|
|
195
|
+
modal.classList.add('visible');
|
|
196
|
+
htmlEditorState.isOpen = true;
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error('Error opening HTML Editor Modal:', error);
|
|
200
|
+
alert('Failed to open HTML Editor. See console for details.');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function getSettingsFromForm() {
|
|
205
|
+
const animationType = document.getElementById('html-animation-type').value;
|
|
206
|
+
const showWireframeCheckbox = document.getElementById('show-wireframe');
|
|
207
|
+
const displayOnMeshCheckbox = document.getElementById('display-on-mesh');
|
|
208
|
+
const rigControlNodeCheckbox = document.getElementById('rig-control-node');
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
previewMode: document.getElementById('html-render-type').value || defaultSettings.previewMode,
|
|
212
|
+
playbackSpeed: parseFloat(document.getElementById('html-playback-speed').value),
|
|
213
|
+
animation: {
|
|
214
|
+
type: animationType
|
|
215
|
+
},
|
|
216
|
+
display: {
|
|
217
|
+
showBorders: showWireframeCheckbox ? showWireframeCheckbox.checked : true,
|
|
218
|
+
displayOnMesh: displayOnMeshCheckbox ? displayOnMeshCheckbox.checked : false,
|
|
219
|
+
rigControlNode: rigControlNodeCheckbox ? rigControlNodeCheckbox.checked : false
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function initHtmlEditorModal() {
|
|
225
|
+
console.log('Initializing HTML Editor Modal');
|
|
226
|
+
|
|
227
|
+
Promise.all([
|
|
228
|
+
initHtmlFormatter().then(() => {
|
|
229
|
+
console.log('HTML formatter initialized');
|
|
230
|
+
}),
|
|
231
|
+
initHtmlLinter().then(() => {
|
|
232
|
+
console.log('HTML linter initialized');
|
|
233
|
+
})
|
|
234
|
+
]).catch(error => {
|
|
235
|
+
console.warn('Error initializing HTML tools:', error);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const modal = document.getElementById('html-editor-modal');
|
|
239
|
+
const closeBtn = document.getElementById('html-editor-close');
|
|
240
|
+
const cancelBtn = document.getElementById('html-editor-cancel');
|
|
241
|
+
const applyBtn = document.getElementById('html-editor-apply');
|
|
242
|
+
const formatBtn = document.getElementById('html-editor-format');
|
|
243
|
+
const previewBtn = document.getElementById('html-editor-preview');
|
|
244
|
+
const resetBtn = document.getElementById('html-editor-reset');
|
|
245
|
+
const textarea = modal ? modal.querySelector('#html-editor-textarea') : null;
|
|
246
|
+
const previewContainer = document.getElementById('html-preview-container');
|
|
247
|
+
const previewContent = document.getElementById('html-preview-content');
|
|
248
|
+
const statusEl = document.getElementById('html-editor-status');
|
|
249
|
+
const errorContainer = document.getElementById('html-editor-errors') || createErrorContainer();
|
|
250
|
+
const dropdownsContainer = document.getElementById('editor-dropdowns-container');
|
|
251
|
+
|
|
252
|
+
const renderTypeSelect = document.getElementById('html-render-type');
|
|
253
|
+
const playbackSpeedSelect = document.getElementById('html-playback-speed');
|
|
254
|
+
const animationTypeSelect = document.getElementById('html-animation-type');
|
|
255
|
+
|
|
256
|
+
const showWireframeCheckbox = document.getElementById('show-wireframe');
|
|
257
|
+
|
|
258
|
+
window.showPreviewBorders = showWireframeCheckbox ? showWireframeCheckbox.checked : true;
|
|
259
|
+
|
|
260
|
+
window.openEmbeddedHtmlEditor = openEmbeddedHtmlEditor;
|
|
261
|
+
window.getHtmlSettingsForMesh = getHtmlSettingsForMesh;
|
|
262
|
+
console.log('Registered global function: window.openEmbeddedHtmlEditor =',
|
|
263
|
+
typeof window.openEmbeddedHtmlEditor === 'function' ? 'Function successfully registered' : 'Failed to register function');
|
|
264
|
+
|
|
265
|
+
if (!modal) {
|
|
266
|
+
console.error('HTML Editor Modal not found in the DOM');
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (renderTypeSelect && renderTypeSelect.value === 'longExposure') {
|
|
271
|
+
const dropdownsContainer = document.getElementById('editor-dropdowns-container');
|
|
272
|
+
if (dropdownsContainer) {
|
|
273
|
+
dropdownsContainer.classList.add('long-exposure-mode');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (listenersInitialized) {
|
|
278
|
+
console.log('HTML Editor Modal event listeners already initialized, skipping');
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
closeBtn.addEventListener('click', closeModal);
|
|
283
|
+
cancelBtn.addEventListener('click', closeModal);
|
|
284
|
+
|
|
285
|
+
modal.addEventListener('click', function(e) {
|
|
286
|
+
if (e.target === modal) {
|
|
287
|
+
closeModal();
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (renderTypeSelect) {
|
|
292
|
+
renderTypeSelect.addEventListener('change', () => {
|
|
293
|
+
const meshId = parseInt(modal.dataset.meshId);
|
|
294
|
+
if (!isNaN(meshId)) {
|
|
295
|
+
const settings = getSettingsFromForm();
|
|
296
|
+
saveSettingsForMesh(meshId, settings);
|
|
297
|
+
showStatus(`Render type set to: ${renderTypeSelect.options[renderTypeSelect.selectedIndex].text}`, 'info');
|
|
298
|
+
|
|
299
|
+
const dropdownsContainer = document.getElementById('editor-dropdowns-container');
|
|
300
|
+
if (dropdownsContainer) {
|
|
301
|
+
if (renderTypeSelect.value === 'longExposure') {
|
|
302
|
+
dropdownsContainer.classList.add('long-exposure-mode');
|
|
303
|
+
} else {
|
|
304
|
+
dropdownsContainer.classList.remove('long-exposure-mode');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (playbackSpeedSelect) {
|
|
312
|
+
playbackSpeedSelect.addEventListener('change', () => {
|
|
313
|
+
const meshId = parseInt(modal.dataset.meshId);
|
|
314
|
+
if (!isNaN(meshId)) {
|
|
315
|
+
const settings = getSettingsFromForm();
|
|
316
|
+
const oldPlaybackSpeed = settings.playbackSpeed || 1.0;
|
|
317
|
+
const newPlaybackSpeed = parseFloat(playbackSpeedSelect.value);
|
|
318
|
+
|
|
319
|
+
saveSettingsForMesh(meshId, settings);
|
|
320
|
+
|
|
321
|
+
if (isPreviewActive) {
|
|
322
|
+
try {
|
|
323
|
+
const css3dIframe = document.getElementById('css3d-panel-iframe');
|
|
324
|
+
if (css3dIframe && css3dIframe.contentDocument) {
|
|
325
|
+
const styleEl = css3dIframe.contentDocument.querySelector('style');
|
|
326
|
+
if (styleEl) {
|
|
327
|
+
styleEl.textContent = styleEl.textContent.replace(
|
|
328
|
+
/animation-duration:\s*[^;]+/,
|
|
329
|
+
`animation-duration: ${1.0/newPlaybackSpeed}s !important`
|
|
330
|
+
).replace(
|
|
331
|
+
/transition-duration:\s*[^;]+/,
|
|
332
|
+
`transition-duration: ${1.0/newPlaybackSpeed}s !important`
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch (err) {
|
|
337
|
+
console.debug('Error updating playback speed in preview:', err);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
showStatus(`Playback speed set to: ${playbackSpeedSelect.options[playbackSpeedSelect.selectedIndex].text}`, 'info');
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (animationTypeSelect) {
|
|
347
|
+
animationTypeSelect.addEventListener('change', () => {
|
|
348
|
+
const meshId = parseInt(modal.dataset.meshId);
|
|
349
|
+
if (!isNaN(meshId)) {
|
|
350
|
+
const settings = getSettingsFromForm();
|
|
351
|
+
saveSettingsForMesh(meshId, settings);
|
|
352
|
+
showStatus(`Animation type set to: ${animationTypeSelect.options[animationTypeSelect.selectedIndex].text}`, 'info');
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (showWireframeCheckbox) {
|
|
358
|
+
showWireframeCheckbox.addEventListener('change', () => {
|
|
359
|
+
window.showPreviewBorders = showWireframeCheckbox.checked;
|
|
360
|
+
|
|
361
|
+
if (isPreviewActive && previewRenderTarget) {
|
|
362
|
+
setLastTextureUpdateTime(0);
|
|
363
|
+
showStatus(`Borders ${showWireframeCheckbox.checked ? 'enabled' : 'disabled'}`, 'info');
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const displayOnMeshCheckbox = document.getElementById('display-on-mesh');
|
|
369
|
+
if (displayOnMeshCheckbox) {
|
|
370
|
+
displayOnMeshCheckbox.addEventListener('change', () => {
|
|
371
|
+
showStatus(`Display on mesh ${displayOnMeshCheckbox.checked ? 'enabled' : 'disabled'}`, 'info');
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const rigControlNodeCheckbox = document.getElementById('rig-control-node');
|
|
376
|
+
if (rigControlNodeCheckbox) {
|
|
377
|
+
rigControlNodeCheckbox.addEventListener('change', () => {
|
|
378
|
+
showStatus(`Rig control node ${rigControlNodeCheckbox.checked ? 'enabled' : 'disabled'}`, 'info');
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
formatBtn.addEventListener('click', async () => {
|
|
383
|
+
try {
|
|
384
|
+
const selectionStart = textarea.selectionStart;
|
|
385
|
+
const selectionEnd = textarea.selectionEnd;
|
|
386
|
+
const hasSelection = selectionStart !== selectionEnd;
|
|
387
|
+
|
|
388
|
+
let htmlToFormat, formattedHtml;
|
|
389
|
+
|
|
390
|
+
if (hasSelection) {
|
|
391
|
+
htmlToFormat = textarea.value.substring(selectionStart, selectionEnd);
|
|
392
|
+
} else {
|
|
393
|
+
htmlToFormat = textarea.value;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
formattedHtml = await externalFormatHtml(htmlToFormat);
|
|
397
|
+
|
|
398
|
+
if (hasSelection) {
|
|
399
|
+
textarea.value =
|
|
400
|
+
textarea.value.substring(0, selectionStart) +
|
|
401
|
+
formattedHtml +
|
|
402
|
+
textarea.value.substring(selectionEnd);
|
|
403
|
+
|
|
404
|
+
textarea.selectionStart = selectionStart;
|
|
405
|
+
textarea.selectionEnd = selectionStart + formattedHtml.length;
|
|
406
|
+
} else {
|
|
407
|
+
textarea.value = formattedHtml;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
showStatus(`${hasSelection ? 'Selection' : 'HTML'} formatted successfully`, 'success');
|
|
411
|
+
|
|
412
|
+
lintHtmlContent();
|
|
413
|
+
} catch (error) {
|
|
414
|
+
showStatus('Error formatting HTML: ' + error.message, 'error');
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
textarea.addEventListener('input', () => {
|
|
419
|
+
clearTimeout(lintDebounceTimer);
|
|
420
|
+
lintDebounceTimer = setTimeout(() => {
|
|
421
|
+
lintHtmlContent();
|
|
422
|
+
}, 500);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
function handlePreviewClick() {
|
|
426
|
+
const modal = document.getElementById('html-editor-modal');
|
|
427
|
+
const textarea = document.getElementById('html-editor-textarea');
|
|
428
|
+
const previewContent = document.getElementById('html-preview-content');
|
|
429
|
+
const errorContainer = document.getElementById('html-editor-errors');
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
const html = textarea.value;
|
|
433
|
+
const meshId = parseInt(modal.dataset.meshId);
|
|
434
|
+
|
|
435
|
+
const renderTypeSelect = document.getElementById('html-render-type');
|
|
436
|
+
let previewMode = renderTypeSelect ? renderTypeSelect.value : 'threejs';
|
|
437
|
+
|
|
438
|
+
const playbackSpeedSelect = document.getElementById('html-playback-speed');
|
|
439
|
+
const playbackSpeed = playbackSpeedSelect ? parseFloat(playbackSpeedSelect.value) : 1.0;
|
|
440
|
+
|
|
441
|
+
const animationTypeSelect = document.getElementById('html-animation-type');
|
|
442
|
+
let animationType = animationTypeSelect ? animationTypeSelect.value : 'play';
|
|
443
|
+
|
|
444
|
+
if (previewMode === 'longExposure') {
|
|
445
|
+
previewMode = 'threejs';
|
|
446
|
+
animationType = 'longExposure';
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const showWireframeCheckbox = document.getElementById('show-wireframe');
|
|
450
|
+
const showPreviewBorders = showWireframeCheckbox ? showWireframeCheckbox.checked : true;
|
|
451
|
+
|
|
452
|
+
const settings = new CustomTextureSettings(
|
|
453
|
+
html,
|
|
454
|
+
meshId,
|
|
455
|
+
previewMode,
|
|
456
|
+
playbackSpeed,
|
|
457
|
+
animationType,
|
|
458
|
+
showPreviewBorders,
|
|
459
|
+
showStatus,
|
|
460
|
+
(error) => showStatus(error, 'error'),
|
|
461
|
+
errorContainer
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
const setModalData = (key, value) => {
|
|
465
|
+
modal.dataset[key] = value;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
initalizePreview(settings, previewContent, setModalData);
|
|
469
|
+
|
|
470
|
+
modal.classList.add('preview-mode');
|
|
471
|
+
|
|
472
|
+
if (renderTypeSelect) {
|
|
473
|
+
const previewModeName = renderTypeSelect.options[renderTypeSelect.selectedIndex].text;
|
|
474
|
+
showStatus(`Preview mode: ${previewModeName}`, 'info');
|
|
475
|
+
}
|
|
476
|
+
} catch (error) {
|
|
477
|
+
showStatus('Error generating preview: ' + error.message, 'error');
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
previewBtn.addEventListener('click', handlePreviewClick);
|
|
482
|
+
|
|
483
|
+
resetBtn.addEventListener('click', () => {
|
|
484
|
+
modal.classList.remove('preview-mode');
|
|
485
|
+
|
|
486
|
+
cleanupThreeJsPreview();
|
|
487
|
+
|
|
488
|
+
const directPreviewIframe = document.getElementById('html-preview-content').querySelector('iframe');
|
|
489
|
+
if (directPreviewIframe) {
|
|
490
|
+
try {
|
|
491
|
+
if (directPreviewIframe.contentDocument) {
|
|
492
|
+
directPreviewIframe.contentDocument.open();
|
|
493
|
+
directPreviewIframe.contentDocument.write('');
|
|
494
|
+
directPreviewIframe.contentDocument.close();
|
|
495
|
+
}
|
|
496
|
+
directPreviewIframe.remove();
|
|
497
|
+
} catch (error) {
|
|
498
|
+
console.debug('Error cleaning up direct preview iframe:', error);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const controlsContainer = document.getElementById('html-preview-content').querySelector('.preview-controls');
|
|
503
|
+
if (controlsContainer) {
|
|
504
|
+
controlsContainer.remove();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
showStatus('Editor view restored', 'info');
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
applyBtn.addEventListener('click', async () => {
|
|
511
|
+
const modal = document.getElementById('html-editor-modal');
|
|
512
|
+
const textarea = document.getElementById('html-editor-textarea');
|
|
513
|
+
const meshId = parseInt(modal.dataset.meshId);
|
|
514
|
+
if (isNaN(meshId)) {
|
|
515
|
+
showStatus('Error: No mesh ID found', 'error');
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const htmlContent = textarea.value;
|
|
519
|
+
try {
|
|
520
|
+
showStatus('Saving HTML content and settings...', 'info');
|
|
521
|
+
await saveHtmlForMesh(meshId, htmlContent);
|
|
522
|
+
htmlEditorState.changesSaved = true;
|
|
523
|
+
updateHtmlIcons();
|
|
524
|
+
showStatus('HTML content and settings saved successfully!', 'success');
|
|
525
|
+
closeModal();
|
|
526
|
+
} catch (error) {
|
|
527
|
+
console.error('Error saving HTML content:', error);
|
|
528
|
+
showStatus(`Error saving: ${error.message}`, 'error');
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
textarea.addEventListener('keydown', function(e) {
|
|
533
|
+
if (e.key === 'Tab') {
|
|
534
|
+
e.preventDefault();
|
|
535
|
+
const start = this.selectionStart;
|
|
536
|
+
const end = this.selectionEnd;
|
|
537
|
+
|
|
538
|
+
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
|
|
539
|
+
|
|
540
|
+
this.selectionStart = this.selectionEnd = start + 4;
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
listenersInitialized = true;
|
|
545
|
+
console.log('HTML Editor Modal event listeners initialized successfully');
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function closeModal() {
|
|
549
|
+
const modal = document.getElementById('html-editor-modal');
|
|
550
|
+
const meshId = parseInt(modal.dataset.meshId);
|
|
551
|
+
|
|
552
|
+
modal.classList.remove('visible');
|
|
553
|
+
htmlEditorState.isOpen = false;
|
|
554
|
+
|
|
555
|
+
cleanupThreeJsPreview();
|
|
556
|
+
|
|
557
|
+
if (!isNaN(meshId) && !htmlEditorState.changesSaved) {
|
|
558
|
+
console.log('Discarding unsaved changes for mesh ID:', meshId);
|
|
559
|
+
htmlEditorState.needsReload = true;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
htmlEditorState.changesSaved = false;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export function showStatus(message, type = 'info') {
|
|
566
|
+
const statusEl = document.getElementById('html-editor-status');
|
|
567
|
+
statusEl.textContent = message;
|
|
568
|
+
statusEl.className = `editor-status ${type}`;
|
|
569
|
+
|
|
570
|
+
const delay = (type === 'success' || type === 'error') ? 5000 : 3000;
|
|
571
|
+
|
|
572
|
+
setTimeout(() => {
|
|
573
|
+
statusEl.textContent = '';
|
|
574
|
+
statusEl.className = 'editor-status';
|
|
575
|
+
}, delay);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function createErrorContainer() {
|
|
579
|
+
const container = document.createElement('div');
|
|
580
|
+
container.id = 'html-editor-errors';
|
|
581
|
+
container.className = 'html-editor-errors';
|
|
582
|
+
container.style.cssText = `
|
|
583
|
+
position: absolute;
|
|
584
|
+
bottom: 0;
|
|
585
|
+
left: 0;
|
|
586
|
+
right: 0;
|
|
587
|
+
max-height: 100px;
|
|
588
|
+
overflow-y: auto;
|
|
589
|
+
background-color: #f8d7da;
|
|
590
|
+
color: #721c24;
|
|
591
|
+
border-top: 1px solid #f5c6cb;
|
|
592
|
+
padding: 8px;
|
|
593
|
+
font-size: 12px;
|
|
594
|
+
display: none;
|
|
595
|
+
`;
|
|
596
|
+
|
|
597
|
+
const modal = document.getElementById('html-editor-modal');
|
|
598
|
+
const editorContainer = modal ? modal.querySelector('.editor-container') : null;
|
|
599
|
+
|
|
600
|
+
if (editorContainer) {
|
|
601
|
+
editorContainer.style.position = 'relative';
|
|
602
|
+
editorContainer.appendChild(container);
|
|
603
|
+
} else {
|
|
604
|
+
if (modal) {
|
|
605
|
+
modal.appendChild(container);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return container;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
async function verifyExtensionExists(glbBuffer, meshId) {
|
|
613
|
+
if (!glbBuffer) {
|
|
614
|
+
console.error('verifyExtensionExists: No GLB buffer provided');
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
try {
|
|
619
|
+
console.log(`Verifying binary extension for mesh ${meshId} in buffer size: ${glbBuffer.byteLength} bytes`);
|
|
620
|
+
|
|
621
|
+
const dataView = new DataView(glbBuffer);
|
|
622
|
+
|
|
623
|
+
if (dataView.byteLength < 12) {
|
|
624
|
+
console.error('Buffer too small for valid GLB');
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const magic = dataView.getUint32(0, true);
|
|
629
|
+
if (magic !== 0x46546C67) {
|
|
630
|
+
console.error(`Invalid GLB magic number: ${magic.toString(16)}`);
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const jsonChunkLength = dataView.getUint32(12, true);
|
|
635
|
+
|
|
636
|
+
const jsonStart = 20;
|
|
637
|
+
const jsonEnd = jsonStart + jsonChunkLength;
|
|
638
|
+
const jsonData = glbBuffer.slice(jsonStart, jsonEnd);
|
|
639
|
+
const jsonString = new TextDecoder('utf-8').decode(jsonData);
|
|
640
|
+
const gltf = JSON.parse(jsonString);
|
|
641
|
+
|
|
642
|
+
if (!gltf.extensions || !gltf.extensions[MESH_BINARY_EXTENSION]) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const associations = gltf.extensions[MESH_BINARY_EXTENSION].meshBinaryAssociations;
|
|
647
|
+
if (!associations || !Array.isArray(associations)) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const association = associations.find(assoc => assoc[MESH_INDEX_PROPERTY] === meshId);
|
|
652
|
+
const found = !!association;
|
|
653
|
+
|
|
654
|
+
if (found) {
|
|
655
|
+
const bufferIndex = association[BINARY_DATA_PROPERTY];
|
|
656
|
+
|
|
657
|
+
if (!gltf.buffers || !gltf.buffers[bufferIndex]) {
|
|
658
|
+
console.log(`Buffer ${bufferIndex} referenced but not found for mesh ${meshId}`);
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
console.log(`Found binary extension for mesh ${meshId} -> buffer ${bufferIndex}`);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return found;
|
|
666
|
+
} catch (error) {
|
|
667
|
+
console.error('Error verifying extension:', error);
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export function resetInitialization() {
|
|
673
|
+
listenersInitialized = false;
|
|
674
|
+
console.log('HTML Editor Modal initialization flag reset');
|
|
675
|
+
}
|