@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,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Texture Debugger - Mesh Panel Module
|
|
3
|
+
*
|
|
4
|
+
* This module handles mesh visibility panel UI and interaction.
|
|
5
|
+
*/
|
|
6
|
+
import { deserializeStringFromBinary, isValidHtml } from '../../../util/data/string-serder.js';
|
|
7
|
+
import { openMeshInfoModal } from '../../../modals/mesh-info-modal/mesh-info-modal.js';
|
|
8
|
+
import { getBinaryBufferForMesh } from '../../../util/data/glb-buffer-manager.js';
|
|
9
|
+
import { getCurrentGlbBuffer } from '../../../util/scene/glb-controller.js';
|
|
10
|
+
import { getState, updateState } from '../../../util/state/scene-state.js';
|
|
11
|
+
import { checkMeshHasHtmlContent } from '../../../util/data/mesh-html-manager.js';
|
|
12
|
+
|
|
13
|
+
// Load mesh panel CSS
|
|
14
|
+
const link = document.createElement('link');
|
|
15
|
+
link.rel = 'stylesheet';
|
|
16
|
+
link.href = '/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css';
|
|
17
|
+
document.head.appendChild(link);
|
|
18
|
+
|
|
19
|
+
// Track meshes with binary content
|
|
20
|
+
export const meshesWithHtml = new Set();
|
|
21
|
+
// Icon color constants
|
|
22
|
+
const ICON_COLORS = {
|
|
23
|
+
HAS_HTML: '#f8d73e', // Yellow color for meshes with binary content (Fallout yellow)
|
|
24
|
+
VALID_HTML: '#4CAF50', // Green color for meshes with valid HTML content
|
|
25
|
+
NO_HTML: '#8a8a8a' // Default color for meshes without content
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Toggle the binary content icon appearance for a specific mesh
|
|
30
|
+
* @param {number} meshIndex - The index of the mesh to toggle
|
|
31
|
+
* @param {boolean} hasHtml - Whether the mesh has binary content
|
|
32
|
+
* @param {boolean} forceUpdate - If true, forces the update without rechecking
|
|
33
|
+
* @returns {boolean} Whether the operation was successful
|
|
34
|
+
*/
|
|
35
|
+
export function toggleMeshCodeIcon(meshIndex, hasHtml, forceUpdate = false) {
|
|
36
|
+
console.log(`Toggling mesh code icon for mesh ${meshIndex} to ${hasHtml ? 'has content' : 'no content'}`);
|
|
37
|
+
|
|
38
|
+
if (hasHtml) {
|
|
39
|
+
meshesWithHtml.add(meshIndex);
|
|
40
|
+
} else {
|
|
41
|
+
meshesWithHtml.delete(meshIndex);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (forceUpdate) {
|
|
45
|
+
if (!window._forcedHtmlStates) {
|
|
46
|
+
window._forcedHtmlStates = {};
|
|
47
|
+
}
|
|
48
|
+
window._forcedHtmlStates[meshIndex] = hasHtml;
|
|
49
|
+
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
if (window._forcedHtmlStates) {
|
|
52
|
+
delete window._forcedHtmlStates[meshIndex];
|
|
53
|
+
}
|
|
54
|
+
}, 500);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const icons = document.querySelectorAll(`.mesh-html-editor-icon[data-mesh-index="${meshIndex}"]`);
|
|
58
|
+
|
|
59
|
+
if (icons.length === 0) {
|
|
60
|
+
console.log(`No icons found for mesh index ${meshIndex}, trying broader search`);
|
|
61
|
+
const allIcons = document.querySelectorAll('.mesh-html-editor-icon');
|
|
62
|
+
|
|
63
|
+
allIcons.forEach((icon) => {
|
|
64
|
+
const iconMeshIndex = parseInt(icon.dataset.meshIndex);
|
|
65
|
+
if (iconMeshIndex === meshIndex) {
|
|
66
|
+
updateIconAppearance(icon, hasHtml);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
} else {
|
|
70
|
+
icons.forEach(icon => {
|
|
71
|
+
updateIconAppearance(icon, hasHtml);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create the mesh visibility panel in the UI
|
|
80
|
+
*/
|
|
81
|
+
export function createMeshVisibilityPanel() {
|
|
82
|
+
window.removeMeshHtmlFlag = removeMeshHtmlFlag;
|
|
83
|
+
window.updateHtmlIcons = updateHtmlIcons;
|
|
84
|
+
window.openMeshInfoModal = openMeshInfoModal;
|
|
85
|
+
|
|
86
|
+
const state = getState();
|
|
87
|
+
const hasMeshes = state.meshes && state.meshes.length > 0;
|
|
88
|
+
|
|
89
|
+
if (!hasMeshes) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
groupMeshesByName();
|
|
94
|
+
|
|
95
|
+
const meshGroupsContainer = document.getElementById('mesh-groups');
|
|
96
|
+
if (!meshGroupsContainer) return;
|
|
97
|
+
|
|
98
|
+
meshGroupsContainer.innerHTML = '';
|
|
99
|
+
|
|
100
|
+
for (const groupName in state.meshGroups) {
|
|
101
|
+
const groupMeshes = state.meshGroups[groupName];
|
|
102
|
+
|
|
103
|
+
const groupDiv = document.createElement('div');
|
|
104
|
+
groupDiv.className = 'mesh-group';
|
|
105
|
+
|
|
106
|
+
const headerDiv = document.createElement('div');
|
|
107
|
+
headerDiv.className = 'mesh-group-header';
|
|
108
|
+
headerDiv.style.display = 'flex';
|
|
109
|
+
headerDiv.style.alignItems = 'center';
|
|
110
|
+
headerDiv.style.justifyContent = 'space-between';
|
|
111
|
+
headerDiv.style.width = '100%';
|
|
112
|
+
headerDiv.style.cursor = 'pointer';
|
|
113
|
+
|
|
114
|
+
const headerLeftDiv = document.createElement('div');
|
|
115
|
+
headerLeftDiv.style.display = 'flex';
|
|
116
|
+
headerLeftDiv.style.alignItems = 'center';
|
|
117
|
+
|
|
118
|
+
const groupToggle = document.createElement('input');
|
|
119
|
+
groupToggle.type = 'checkbox';
|
|
120
|
+
groupToggle.className = 'mesh-group-toggle';
|
|
121
|
+
groupToggle.checked = true;
|
|
122
|
+
groupToggle.dataset.group = groupName;
|
|
123
|
+
|
|
124
|
+
groupToggle.addEventListener('change', (e) => {
|
|
125
|
+
const isVisible = e.target.checked;
|
|
126
|
+
toggleMeshGroupVisibility(groupName, isVisible);
|
|
127
|
+
|
|
128
|
+
const meshToggles = groupDiv.querySelectorAll('.mesh-toggle');
|
|
129
|
+
meshToggles.forEach(toggle => {
|
|
130
|
+
toggle.checked = isVisible;
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
groupToggle.addEventListener('click', (e) => {
|
|
135
|
+
e.stopPropagation();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const groupNameSpan = document.createElement('span');
|
|
139
|
+
groupNameSpan.className = 'mesh-group-name';
|
|
140
|
+
groupNameSpan.textContent = groupName + ' ';
|
|
141
|
+
|
|
142
|
+
const groupCountSpan = document.createElement('span');
|
|
143
|
+
groupCountSpan.className = 'mesh-group-count';
|
|
144
|
+
groupCountSpan.textContent = `(${groupMeshes.length})`;
|
|
145
|
+
|
|
146
|
+
const collapseBtn = document.createElement('span');
|
|
147
|
+
collapseBtn.className = 'mesh-group-collapse';
|
|
148
|
+
collapseBtn.textContent = '+';
|
|
149
|
+
collapseBtn.style.cursor = 'pointer';
|
|
150
|
+
collapseBtn.style.marginLeft = 'auto';
|
|
151
|
+
|
|
152
|
+
const toggleCollapse = () => {
|
|
153
|
+
const meshItemsDiv = groupDiv.querySelector('.mesh-items');
|
|
154
|
+
const isCollapsed = meshItemsDiv.style.display === 'none';
|
|
155
|
+
|
|
156
|
+
meshItemsDiv.style.display = isCollapsed ? 'block' : 'none';
|
|
157
|
+
collapseBtn.textContent = isCollapsed ? '-' : '+';
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
headerDiv.addEventListener('click', toggleCollapse);
|
|
161
|
+
|
|
162
|
+
collapseBtn.addEventListener('click', (e) => {
|
|
163
|
+
e.stopPropagation();
|
|
164
|
+
toggleCollapse();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
headerLeftDiv.appendChild(groupToggle);
|
|
168
|
+
headerLeftDiv.appendChild(groupNameSpan);
|
|
169
|
+
headerLeftDiv.appendChild(groupCountSpan);
|
|
170
|
+
|
|
171
|
+
headerDiv.appendChild(headerLeftDiv);
|
|
172
|
+
headerDiv.appendChild(collapseBtn);
|
|
173
|
+
|
|
174
|
+
const meshItemsDiv = document.createElement('div');
|
|
175
|
+
meshItemsDiv.className = 'mesh-items';
|
|
176
|
+
meshItemsDiv.style.display = 'none';
|
|
177
|
+
|
|
178
|
+
const meshPromises = groupMeshes.map(async (mesh, index) => {
|
|
179
|
+
const meshDiv = document.createElement('div');
|
|
180
|
+
meshDiv.className = 'mesh-item';
|
|
181
|
+
meshDiv.style.display = 'flex';
|
|
182
|
+
meshDiv.style.alignItems = 'center';
|
|
183
|
+
|
|
184
|
+
const meshToggle = document.createElement('input');
|
|
185
|
+
meshToggle.type = 'checkbox';
|
|
186
|
+
meshToggle.className = 'mesh-toggle';
|
|
187
|
+
meshToggle.checked = mesh.visible;
|
|
188
|
+
const meshIndex = state.meshes.indexOf(mesh);
|
|
189
|
+
meshToggle.dataset.meshIndex = meshIndex;
|
|
190
|
+
|
|
191
|
+
meshToggle.addEventListener('change', (e) => {
|
|
192
|
+
const isVisible = e.target.checked;
|
|
193
|
+
const meshIndex = parseInt(e.target.dataset.meshIndex);
|
|
194
|
+
|
|
195
|
+
if (!isNaN(meshIndex) && meshIndex >= 0 && meshIndex < state.meshes.length) {
|
|
196
|
+
state.meshes[meshIndex].visible = isVisible;
|
|
197
|
+
updateGroupToggleState(groupName);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const meshNameSpan = document.createElement('span');
|
|
202
|
+
meshNameSpan.className = 'mesh-name';
|
|
203
|
+
meshNameSpan.textContent = getMeshDisplayName(mesh);
|
|
204
|
+
meshNameSpan.title = mesh.name || "Unnamed mesh";
|
|
205
|
+
meshNameSpan.style.flexGrow = '1';
|
|
206
|
+
|
|
207
|
+
const htmlEditorIcon = document.createElement('span');
|
|
208
|
+
htmlEditorIcon.className = 'mesh-html-editor-icon';
|
|
209
|
+
htmlEditorIcon.title = 'Edit HTML';
|
|
210
|
+
htmlEditorIcon.dataset.meshIndex = meshIndex;
|
|
211
|
+
htmlEditorIcon.innerHTML = `
|
|
212
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
|
|
213
|
+
<path d="M234.8 511.7L196 500.4c-4.2-1.2-6.7-5.7-5.5-9.9L331.3 5.8c1.2-4.2 5.7-6.7 9.9-5.5L380 11.6c4.2 1.2 6.7 5.7 5.5 9.9L244.7 506.2c-1.2 4.3-5.6 6.7-9.9 5.5zm-83.2-121.1l27.2-29c3.1-3.3 2.8-8.5-.5-11.5L72.2 256l106.1-94.1c3.4-3 3.6-8.2.5-11.5l-27.2-29c-3-3.2-8.1-3.4-11.3-.4L2.5 250.2c-3.4 3.2-3.4 8.5 0 11.7L140.3 391c3.2 3 8.2 2.8 11.3-.4zm284.1.4l137.7-129.1c3.4-3.2 3.4-8.5 0-11.7L435.7 121c-3.2-3-8.3-2.9-11.3.4l-27.2 29c-3.1 3.3-2.8 8.5.5 11.5L503.8 256l-106.1 94.1c-3.4 3-3.6 8.2-.5 11.5l27.2 29c3.1 3.2 8.1 3.4 11.3.4z"/>
|
|
214
|
+
</svg>
|
|
215
|
+
`;
|
|
216
|
+
|
|
217
|
+
const hasHtml = await checkMeshHasHtmlContent(meshIndex);
|
|
218
|
+
htmlEditorIcon.dataset.hasHtml = hasHtml;
|
|
219
|
+
updateIconAppearance(htmlEditorIcon, hasHtml);
|
|
220
|
+
|
|
221
|
+
htmlEditorIcon.addEventListener('click', (e) => {
|
|
222
|
+
e.stopPropagation();
|
|
223
|
+
console.log('HTML editor icon clicked');
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const meshIndex = parseInt(meshToggle.dataset.meshIndex);
|
|
227
|
+
const meshName = state.meshes[meshIndex].name || 'Unnamed mesh';
|
|
228
|
+
|
|
229
|
+
console.log(`Opening HTML editor for mesh: ${meshName} (index: ${meshIndex})`);
|
|
230
|
+
|
|
231
|
+
if (typeof window.openEmbeddedHtmlEditor === 'function') {
|
|
232
|
+
window.openEmbeddedHtmlEditor(meshName, meshIndex);
|
|
233
|
+
} else {
|
|
234
|
+
console.error('HTML Editor function not found. Modal may not be initialized yet.');
|
|
235
|
+
alert('HTML Editor not ready. Please try again in a moment.');
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error('Error opening HTML editor modal:', error);
|
|
239
|
+
alert('Error opening HTML editor. See console for details.');
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const meshInfoIcon = document.createElement('span');
|
|
244
|
+
meshInfoIcon.className = 'mesh-info-icon';
|
|
245
|
+
meshInfoIcon.title = 'View mesh details';
|
|
246
|
+
meshInfoIcon.dataset.meshIndex = meshIndex;
|
|
247
|
+
meshInfoIcon.innerHTML = `
|
|
248
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14">
|
|
249
|
+
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
250
|
+
<line x1="12" y1="16" x2="12" y2="12" stroke="currentColor" stroke-width="2"/>
|
|
251
|
+
<circle cx="12" cy="8" r="1" fill="currentColor"/>
|
|
252
|
+
</svg>
|
|
253
|
+
`;
|
|
254
|
+
meshInfoIcon.style.color = '#6c757d';
|
|
255
|
+
meshInfoIcon.style.cursor = 'pointer';
|
|
256
|
+
meshInfoIcon.style.marginLeft = '8px';
|
|
257
|
+
|
|
258
|
+
meshInfoIcon.addEventListener('click', (e) => {
|
|
259
|
+
e.stopPropagation();
|
|
260
|
+
console.log('Mesh info icon clicked for mesh index:', meshIndex);
|
|
261
|
+
try {
|
|
262
|
+
const meshName = state.meshes[meshIndex].name || 'Unnamed mesh';
|
|
263
|
+
console.log(`Opening mesh info modal for mesh: ${meshName} (index: ${meshIndex})`);
|
|
264
|
+
if (typeof window.openMeshInfoModal === 'function') {
|
|
265
|
+
window.openMeshInfoModal(meshName, meshIndex);
|
|
266
|
+
} else {
|
|
267
|
+
console.error('Mesh Info Modal function not found. Modal may not be initialized yet.');
|
|
268
|
+
alert('Mesh Info Modal not ready. Please try again in a moment.');
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error('Error opening mesh info modal:', error);
|
|
272
|
+
alert('Error opening mesh info modal. See console for details.');
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const iconsContainer = document.createElement('div');
|
|
277
|
+
iconsContainer.className = 'mesh-item-icons';
|
|
278
|
+
|
|
279
|
+
if (mesh.name && mesh.name.toLowerCase().includes('display')) {
|
|
280
|
+
iconsContainer.appendChild(htmlEditorIcon);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
iconsContainer.appendChild(meshInfoIcon);
|
|
284
|
+
|
|
285
|
+
meshDiv.appendChild(meshToggle);
|
|
286
|
+
meshDiv.appendChild(meshNameSpan);
|
|
287
|
+
meshDiv.appendChild(iconsContainer);
|
|
288
|
+
|
|
289
|
+
return meshDiv;
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
Promise.all(meshPromises).then(meshDivs => {
|
|
293
|
+
meshDivs.forEach(div => {
|
|
294
|
+
meshItemsDiv.appendChild(div);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
groupDiv.appendChild(headerDiv);
|
|
298
|
+
groupDiv.appendChild(meshItemsDiv);
|
|
299
|
+
|
|
300
|
+
meshGroupsContainer.appendChild(groupDiv);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Update binary content icons to reflect current content
|
|
307
|
+
* This should be called after saving binary data
|
|
308
|
+
*/
|
|
309
|
+
export function updateHtmlIcons() {
|
|
310
|
+
document.querySelectorAll('.mesh-html-editor-icon').forEach(async (icon) => {
|
|
311
|
+
const meshIndex = parseInt(icon.dataset.meshIndex);
|
|
312
|
+
if (!isNaN(meshIndex)) {
|
|
313
|
+
const hasHtml = await checkMeshHasHtmlContent(meshIndex);
|
|
314
|
+
toggleMeshCodeIcon(meshIndex, hasHtml);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Group meshes by name prefix
|
|
321
|
+
*/
|
|
322
|
+
function groupMeshesByName() {
|
|
323
|
+
const state = getState();
|
|
324
|
+
const meshGroups = {};
|
|
325
|
+
|
|
326
|
+
state.meshes.forEach(mesh => {
|
|
327
|
+
const groupName = getGroupName(mesh);
|
|
328
|
+
|
|
329
|
+
if (!meshGroups[groupName]) {
|
|
330
|
+
meshGroups[groupName] = [];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
meshGroups[groupName].push(mesh);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
updateState('meshGroups', meshGroups);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get group name from mesh based on naming pattern
|
|
341
|
+
* @param {THREE.Mesh} mesh - The mesh to get group name for
|
|
342
|
+
* @returns {string} Group name for the mesh
|
|
343
|
+
*/
|
|
344
|
+
function getGroupName(mesh) {
|
|
345
|
+
const name = mesh.name || 'Unnamed';
|
|
346
|
+
|
|
347
|
+
const patterns = [
|
|
348
|
+
/^([^_]+)_.*$/, // Anything before first underscore
|
|
349
|
+
/^([^.]+)\..*$/, // Anything before first period
|
|
350
|
+
/^([^0-9]+).*$/ // Anything before first number
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
for (const pattern of patterns) {
|
|
354
|
+
const match = name.match(pattern);
|
|
355
|
+
if (match && match[1]) {
|
|
356
|
+
return match[1];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (name === 'Unnamed' || name === 'Cube') {
|
|
361
|
+
return 'Default';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return name.substring(0, 4);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get display name for a mesh
|
|
369
|
+
* @param {THREE.Mesh} mesh - The mesh to get display name for
|
|
370
|
+
* @returns {string} Display name for the mesh
|
|
371
|
+
*/
|
|
372
|
+
function getMeshDisplayName(mesh) {
|
|
373
|
+
return mesh.name || "Unnamed mesh";
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Toggle visibility of all meshes in a group
|
|
378
|
+
* @param {string} groupName - The name of the group to toggle
|
|
379
|
+
* @param {boolean} isVisible - Whether the meshes should be visible
|
|
380
|
+
*/
|
|
381
|
+
export function toggleMeshGroupVisibility(groupName, isVisible) {
|
|
382
|
+
const state = getState();
|
|
383
|
+
if (state.meshGroups[groupName]) {
|
|
384
|
+
state.meshGroups[groupName].forEach(mesh => {
|
|
385
|
+
mesh.visible = isVisible;
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Update group toggle state based on individual mesh visibility
|
|
392
|
+
* @param {string} groupName - The name of the group to update
|
|
393
|
+
*/
|
|
394
|
+
export function updateGroupToggleState(groupName) {
|
|
395
|
+
const groupToggle = document.querySelector(`.mesh-group-toggle[data-group="${groupName}"]`);
|
|
396
|
+
const state = getState();
|
|
397
|
+
|
|
398
|
+
if (!groupToggle || !state.meshGroups[groupName]) return;
|
|
399
|
+
|
|
400
|
+
const allVisible = state.meshGroups[groupName].every(mesh => mesh.visible);
|
|
401
|
+
const anyVisible = state.meshGroups[groupName].some(mesh => mesh.visible);
|
|
402
|
+
|
|
403
|
+
if (anyVisible && !allVisible) {
|
|
404
|
+
groupToggle.indeterminate = true;
|
|
405
|
+
} else {
|
|
406
|
+
groupToggle.indeterminate = false;
|
|
407
|
+
groupToggle.checked = allVisible;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Remove binary content flag for a specific mesh
|
|
413
|
+
* @param {number} meshIndex - The index of the mesh to update
|
|
414
|
+
*/
|
|
415
|
+
export function removeMeshHtmlFlag(meshIndex) {
|
|
416
|
+
console.log(`Removing binary content flag for mesh index ${meshIndex}`);
|
|
417
|
+
|
|
418
|
+
toggleMeshCodeIcon(meshIndex, false, true);
|
|
419
|
+
|
|
420
|
+
checkMeshHasHtmlContent(meshIndex).then(() => {
|
|
421
|
+
console.log(`Re-checked mesh binary content status for mesh ${meshIndex}`);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Update an icon's appearance based on content status
|
|
427
|
+
* @param {HTMLElement} icon - The icon element to update
|
|
428
|
+
* @param {boolean} hasHtml - Whether the mesh has content
|
|
429
|
+
*/
|
|
430
|
+
function updateIconAppearance(icon, hasHtml) {
|
|
431
|
+
icon.dataset.hasHtml = hasHtml ? 'true' : 'false';
|
|
432
|
+
|
|
433
|
+
if (hasHtml) {
|
|
434
|
+
icon.classList.add('has-html');
|
|
435
|
+
|
|
436
|
+
const meshIndex = parseInt(icon.dataset.meshIndex);
|
|
437
|
+
|
|
438
|
+
icon.style.color = ICON_COLORS.HAS_HTML;
|
|
439
|
+
icon.title = 'Edit content (has content)';
|
|
440
|
+
|
|
441
|
+
if (!isNaN(meshIndex)) {
|
|
442
|
+
(async () => {
|
|
443
|
+
try {
|
|
444
|
+
const glbBuffer = getCurrentGlbBuffer();
|
|
445
|
+
if (glbBuffer) {
|
|
446
|
+
const binaryBuffer = await getBinaryBufferForMesh(glbBuffer, meshIndex);
|
|
447
|
+
if (binaryBuffer) {
|
|
448
|
+
const result = deserializeStringFromBinary(binaryBuffer);
|
|
449
|
+
const content = result.content;
|
|
450
|
+
if (isValidHtml(content)) {
|
|
451
|
+
icon.style.color = ICON_COLORS.VALID_HTML;
|
|
452
|
+
icon.title = 'Edit content (valid HTML)';
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.error('Error checking if content is valid HTML:', error);
|
|
458
|
+
}
|
|
459
|
+
})();
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
icon.classList.remove('has-html');
|
|
463
|
+
icon.style.color = ICON_COLORS.NO_HTML;
|
|
464
|
+
icon.title = 'Edit content';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
console.log(`Updated icon appearance, hasContent=${hasHtml}, color=${icon.style.color}`);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Force update mesh binary icon state - to be called from html-editor-modal.js
|
|
472
|
+
* This avoids race conditions when saving/removing binary content
|
|
473
|
+
* @param {number} meshIndex - The index of the mesh to update
|
|
474
|
+
* @param {boolean} hasHtml - Whether the mesh has binary content
|
|
475
|
+
*/
|
|
476
|
+
export function forceUpdateMeshHtmlState(meshIndex, hasHtml) {
|
|
477
|
+
return toggleMeshCodeIcon(meshIndex, hasHtml, true);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export default {
|
|
481
|
+
createMeshVisibilityPanel,
|
|
482
|
+
toggleMeshGroupVisibility,
|
|
483
|
+
updateGroupToggleState,
|
|
484
|
+
toggleMeshCodeIcon,
|
|
485
|
+
forceUpdateMeshHtmlState
|
|
486
|
+
};
|