@littlecarlito/blorktools 0.50.3 → 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,696 @@
|
|
|
1
|
+
import { showStatus } from "../../../modals/html-editor-modal/html-editor-modal";
|
|
2
|
+
import { createMeshInfoPanel } from "../../../widgets/mesh-info-widget";
|
|
3
|
+
import { updateMeshTexture } from "../playback/animation-playback-controller";
|
|
4
|
+
import {
|
|
5
|
+
animationCaptureStartTime,
|
|
6
|
+
animationDuration,
|
|
7
|
+
animationPlaybackStartTime,
|
|
8
|
+
finalProgressAnimation,
|
|
9
|
+
finalProgressDuration,
|
|
10
|
+
finalProgressStartTime,
|
|
11
|
+
isAnimationFinite,
|
|
12
|
+
isPreviewActive,
|
|
13
|
+
preRenderedFrames,
|
|
14
|
+
preRenderingInProgress,
|
|
15
|
+
preRenderMaxDuration,
|
|
16
|
+
setAnimationCaptureStartTime,
|
|
17
|
+
setAnimationDuration,
|
|
18
|
+
setAnimationPlaybackStartTime,
|
|
19
|
+
setFinalProgressAnimation,
|
|
20
|
+
setFinalProgressStartTime,
|
|
21
|
+
setIsAnimationFinite,
|
|
22
|
+
setIsPreviewAnimationPaused,
|
|
23
|
+
setPreRenderedFrames,
|
|
24
|
+
setPreRenderingInProgress
|
|
25
|
+
} from "../../state/animation-state";
|
|
26
|
+
import { logAnimationAnalysisReport } from "../../state/log-util";
|
|
27
|
+
import { injectUnifiedAnimationDetectionScript } from "../../data/animation-classifier";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Start pre-rendering for CSS3D content
|
|
31
|
+
* @param {HTMLIFrameElement} iframe - The iframe containing the content
|
|
32
|
+
* @param {Function} callback - Function to call when pre-rendering is complete
|
|
33
|
+
* @param {HTMLElement} progressBar - Optional progress bar element to update
|
|
34
|
+
* @param {Object} settings - Optional settings object
|
|
35
|
+
* @param {THREE.Mesh} previewPlane - The mesh to apply textures to
|
|
36
|
+
*/
|
|
37
|
+
export function startCss3dPreRendering(iframe, callback, progressBar = null, settings = null, previewPlane = null) {
|
|
38
|
+
if (!iframe) {
|
|
39
|
+
console.error('No iframe provided for CSS3D pre-rendering');
|
|
40
|
+
if (callback) callback();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Reset and initialize state
|
|
45
|
+
setPreRenderingInProgress(true);
|
|
46
|
+
setPreRenderedFrames([]);
|
|
47
|
+
|
|
48
|
+
// Tracking variables
|
|
49
|
+
let domSnapshotFrames = [];
|
|
50
|
+
const preRenderStartTime = Date.now();
|
|
51
|
+
|
|
52
|
+
// Progress tracking
|
|
53
|
+
let lastProgressUpdate = 0;
|
|
54
|
+
let progressUpdateInterval = 100; // Update progress every 100ms
|
|
55
|
+
let maxProgressBeforeFinalAnimation = 92; // Cap progress at this value until final animation
|
|
56
|
+
setFinalProgressAnimation(false);
|
|
57
|
+
setFinalProgressStartTime(0);
|
|
58
|
+
|
|
59
|
+
// Analysis metrics tracking
|
|
60
|
+
let loopDetected = false;
|
|
61
|
+
let endDetected = false;
|
|
62
|
+
let analysisMetrics = {};
|
|
63
|
+
let detectedLoopSize = 0;
|
|
64
|
+
|
|
65
|
+
// Track the last capture time to pace captures similar to image2texture
|
|
66
|
+
let lastCaptureTime = 0;
|
|
67
|
+
let captureInterval = 350; // Start with 350ms between captures
|
|
68
|
+
|
|
69
|
+
// Track total frames estimate
|
|
70
|
+
let totalFramesEstimate = 120; // Initial estimate
|
|
71
|
+
|
|
72
|
+
console.log('Starting CSS3D pre-rendering analysis...');
|
|
73
|
+
|
|
74
|
+
// Get animation settings from passed settings object instead of DOM
|
|
75
|
+
let isLongExposureMode = false;
|
|
76
|
+
let playbackSpeed = 1.0;
|
|
77
|
+
|
|
78
|
+
if (settings) {
|
|
79
|
+
// Use settings parameters instead of DOM elements
|
|
80
|
+
isLongExposureMode = settings.isLongExposureMode;
|
|
81
|
+
playbackSpeed = settings.playbackSpeed || 1.0;
|
|
82
|
+
} else {
|
|
83
|
+
// Fallback to DOM access if settings not provided (for backward compatibility)
|
|
84
|
+
const animationTypeSelect = document.getElementById('html-animation-type');
|
|
85
|
+
isLongExposureMode = animationTypeSelect && animationTypeSelect.value === 'longExposure';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Set flag if we're capturing for long exposure
|
|
89
|
+
if (isLongExposureMode) {
|
|
90
|
+
setCapturingForLongExposure(true);
|
|
91
|
+
|
|
92
|
+
// Temporarily disable borders during capture
|
|
93
|
+
const originalBorderSetting = window.showPreviewBorders;
|
|
94
|
+
window.showPreviewBorders = false;
|
|
95
|
+
console.log('Borders temporarily disabled for long exposure capture');
|
|
96
|
+
|
|
97
|
+
// Store original setting to restore later
|
|
98
|
+
window._originalBorderSetting = originalBorderSetting;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Function to update progress bar
|
|
102
|
+
const updateProgress = (percent) => {
|
|
103
|
+
if (progressBar) {
|
|
104
|
+
// Ensure progress never exceeds maxProgressBeforeFinalAnimation unless in final animation
|
|
105
|
+
if (!finalProgressAnimation && percent > maxProgressBeforeFinalAnimation) {
|
|
106
|
+
percent = maxProgressBeforeFinalAnimation;
|
|
107
|
+
}
|
|
108
|
+
progressBar.style.width = `${percent}%`;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Function to create the long exposure texture and apply it
|
|
113
|
+
const createAndApplyCss3dLongExposure = () => {
|
|
114
|
+
if (domSnapshotFrames.length > 0) {
|
|
115
|
+
// Use playbackSpeed from settings instead of DOM
|
|
116
|
+
const longExposureTexture = createLongExposureTexture(domSnapshotFrames, playbackSpeed);
|
|
117
|
+
|
|
118
|
+
// Update the iframe with the long exposure texture
|
|
119
|
+
if (previewPlane) {
|
|
120
|
+
updateMeshTexture(longExposureTexture, previewPlane);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Show a message about the long exposure
|
|
124
|
+
showStatus(`CSS3D Long exposure created from ${domSnapshotFrames.length} frames`, 'success');
|
|
125
|
+
|
|
126
|
+
// Pause animation since we just want to display the static image
|
|
127
|
+
setIsPreviewAnimationPaused(true);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Function to start final progress animation
|
|
132
|
+
const startFinalProgressAnimation = () => {
|
|
133
|
+
if (finalProgressAnimation) return; // Already animating
|
|
134
|
+
|
|
135
|
+
setFinalProgressAnimation(true);
|
|
136
|
+
setFinalProgressStartTime(Date.now());
|
|
137
|
+
|
|
138
|
+
// Start the animation loop
|
|
139
|
+
animateFinalProgress();
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Function to animate progress to 100%
|
|
143
|
+
const animateFinalProgress = () => {
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const elapsed = now - finalProgressStartTime;
|
|
146
|
+
|
|
147
|
+
if (elapsed >= finalProgressDuration) {
|
|
148
|
+
// Animation complete, set to 100%
|
|
149
|
+
updateProgress(100);
|
|
150
|
+
|
|
151
|
+
// Log animation analysis report
|
|
152
|
+
logAnimationAnalysisReport('CSS3D', {
|
|
153
|
+
frameCount: domSnapshotFrames.length,
|
|
154
|
+
duration: animationDuration,
|
|
155
|
+
isFinite: isAnimationFinite,
|
|
156
|
+
loopDetected,
|
|
157
|
+
endDetected,
|
|
158
|
+
analysisTime: now - preRenderStartTime,
|
|
159
|
+
metrics: {
|
|
160
|
+
...analysisMetrics,
|
|
161
|
+
loopSize: detectedLoopSize,
|
|
162
|
+
domSnapshotCount: domSnapshotFrames.length
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Store the DOM snapshot frames in the preRenderedFrames array for compatibility
|
|
167
|
+
setPreRenderedFrames(domSnapshotFrames);
|
|
168
|
+
|
|
169
|
+
// Hide loading overlay with fade out
|
|
170
|
+
const loadingOverlay = document.getElementById('pre-rendering-overlay');
|
|
171
|
+
if (loadingOverlay) {
|
|
172
|
+
loadingOverlay.style.transition = 'opacity 0.5s ease';
|
|
173
|
+
loadingOverlay.style.opacity = '0';
|
|
174
|
+
|
|
175
|
+
// Remove after fade out
|
|
176
|
+
setTimeout(() => {
|
|
177
|
+
if (loadingOverlay.parentNode) {
|
|
178
|
+
loadingOverlay.parentNode.removeChild(loadingOverlay);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// CRITICAL: Initialize playback timing for ALL animation types
|
|
182
|
+
initializePlaybackTiming();
|
|
183
|
+
|
|
184
|
+
// Now create the info panel after pre-rendering is complete
|
|
185
|
+
const canvasContainer = document.querySelector('#html-preview-content');
|
|
186
|
+
if (canvasContainer) {
|
|
187
|
+
const modal = document.getElementById('html-editor-modal');
|
|
188
|
+
const currentMeshId = parseInt(modal.dataset.meshId);
|
|
189
|
+
createMeshInfoPanel(canvasContainer, currentMeshId);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Handle different animation types
|
|
193
|
+
if (isLongExposureMode && preRenderedFrames.length > 0) {
|
|
194
|
+
// For long exposure, create the static image
|
|
195
|
+
const longExposureTexture = createLongExposureTexture(preRenderedFrames, playbackSpeed);
|
|
196
|
+
|
|
197
|
+
if (previewPlane) {
|
|
198
|
+
updateMeshTexture(longExposureTexture, previewPlane);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
showStatus(`Long exposure created from ${preRenderedFrames.length} frames`, 'success');
|
|
202
|
+
setIsPreviewAnimationPaused(true);
|
|
203
|
+
} else {
|
|
204
|
+
// For all other types, start playback
|
|
205
|
+
setIsPreviewAnimationPaused(false);
|
|
206
|
+
|
|
207
|
+
const typeDescription = isAnimationFinite ?
|
|
208
|
+
`finite (${(animationDuration/1000).toFixed(1)}s)` :
|
|
209
|
+
'static/infinite';
|
|
210
|
+
|
|
211
|
+
showStatus(`Animation playback starting - ${typeDescription}, ${preRenderedFrames.length} frames at ${playbackSpeed}x speed`, 'success');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Execute the callback if provided
|
|
215
|
+
if (typeof callback === 'function') {
|
|
216
|
+
callback();
|
|
217
|
+
}
|
|
218
|
+
}, 500);
|
|
219
|
+
|
|
220
|
+
// Don't continue the animation
|
|
221
|
+
return;
|
|
222
|
+
} else {
|
|
223
|
+
// If no loading overlay found, still execute the callback
|
|
224
|
+
if (typeof callback === 'function') {
|
|
225
|
+
console.log('Executing CSS3D pre-rendering callback (no overlay)');
|
|
226
|
+
callback();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
// Calculate progress based on easing function
|
|
231
|
+
const progress = easeOutCubic(elapsed / finalProgressDuration);
|
|
232
|
+
const currentProgress = maxProgressBeforeFinalAnimation + (100 - maxProgressBeforeFinalAnimation) * progress;
|
|
233
|
+
updateProgress(currentProgress);
|
|
234
|
+
|
|
235
|
+
// Update loading text
|
|
236
|
+
const progressText = document.getElementById('loading-progress-text');
|
|
237
|
+
if (progressText) {
|
|
238
|
+
if (isLongExposureMode) {
|
|
239
|
+
progressText.textContent = 'Creating CSS3D long exposure...';
|
|
240
|
+
} else {
|
|
241
|
+
progressText.textContent = 'Finalizing CSS3D animation...';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Continue animation
|
|
246
|
+
requestAnimationFrame(animateFinalProgress);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// Easing function for smooth animation
|
|
251
|
+
const easeOutCubic = (x) => {
|
|
252
|
+
return 1 - Math.pow(1 - x, 3);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Create a DOM snapshot from the iframe
|
|
256
|
+
const createDomSnapshot = (iframe) => {
|
|
257
|
+
try {
|
|
258
|
+
if (!iframe || !iframe.contentDocument || !iframe.contentDocument.documentElement) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Clone the DOM for snapshot
|
|
263
|
+
const domClone = iframe.contentDocument.documentElement.cloneNode(true);
|
|
264
|
+
|
|
265
|
+
// Calculate a hash of the DOM to detect changes
|
|
266
|
+
const domHash = calculateDomHash(domClone);
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
domSnapshot: domClone,
|
|
270
|
+
hash: domHash
|
|
271
|
+
};
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error('Error creating DOM snapshot:', error);
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Calculate a hash of the DOM to detect changes
|
|
279
|
+
const calculateDomHash = (domElement) => {
|
|
280
|
+
try {
|
|
281
|
+
// Extract relevant attributes for comparison
|
|
282
|
+
const attributes = [];
|
|
283
|
+
|
|
284
|
+
// Track animation state separately
|
|
285
|
+
let hasAnimations = false;
|
|
286
|
+
let hasTransitions = false;
|
|
287
|
+
|
|
288
|
+
// Process element and its children recursively
|
|
289
|
+
const processElement = (element) => {
|
|
290
|
+
// Skip script elements
|
|
291
|
+
if (element.tagName === 'SCRIPT') return;
|
|
292
|
+
|
|
293
|
+
// Get element attributes
|
|
294
|
+
const tagName = element.tagName || '';
|
|
295
|
+
const className = element.className || '';
|
|
296
|
+
const id = element.id || '';
|
|
297
|
+
const style = element.style ? element.style.cssText : '';
|
|
298
|
+
|
|
299
|
+
// Check for animations and transitions in style
|
|
300
|
+
if (style.includes('animation') || style.includes('keyframes')) {
|
|
301
|
+
hasAnimations = true;
|
|
302
|
+
}
|
|
303
|
+
if (style.includes('transition')) {
|
|
304
|
+
hasTransitions = true;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Extract more detailed style information
|
|
308
|
+
const transform = style.match(/transform:[^;]+/) || '';
|
|
309
|
+
const opacity = style.match(/opacity:[^;]+/) || '';
|
|
310
|
+
const position = style.match(/((left|top|right|bottom):[^;]+)/) || '';
|
|
311
|
+
const animation = style.match(/animation:[^;]+/) || '';
|
|
312
|
+
const transition = style.match(/transition:[^;]+/) || '';
|
|
313
|
+
const backgroundColor = style.match(/background-color:[^;]+/) || '';
|
|
314
|
+
const color = style.match(/color:[^;]+/) || '';
|
|
315
|
+
|
|
316
|
+
// Add element info to attributes array with more details
|
|
317
|
+
attributes.push(`${tagName}#${id}.${className}[${transform}][${opacity}][${position}][${animation}][${transition}][${backgroundColor}][${color}]`);
|
|
318
|
+
|
|
319
|
+
// Process child elements
|
|
320
|
+
if (element.children) {
|
|
321
|
+
for (let i = 0; i < element.children.length; i++) {
|
|
322
|
+
processElement(element.children[i]);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// Start processing from the root element
|
|
328
|
+
processElement(domElement);
|
|
329
|
+
|
|
330
|
+
// Join all attributes and hash them
|
|
331
|
+
const attributesString = attributes.join('|');
|
|
332
|
+
|
|
333
|
+
// Add animation state to the hash
|
|
334
|
+
const stateInfo = `animations:${hasAnimations}|transitions:${hasTransitions}`;
|
|
335
|
+
const fullString = attributesString + '|' + stateInfo;
|
|
336
|
+
|
|
337
|
+
// Create a simple hash
|
|
338
|
+
let hash = 0;
|
|
339
|
+
for (let i = 0; i < fullString.length; i++) {
|
|
340
|
+
const char = fullString.charCodeAt(i);
|
|
341
|
+
hash = ((hash << 5) - hash) + char;
|
|
342
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return hash.toString();
|
|
346
|
+
} catch (e) {
|
|
347
|
+
console.error('Error calculating DOM hash:', e);
|
|
348
|
+
return Math.random().toString(); // Fallback to random hash
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Calculate the difference between two DOM hashes
|
|
353
|
+
const calculateDomHashDifference = (hash1, hash2) => {
|
|
354
|
+
if (!hash1 || !hash2) return 1;
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
// Convert hashes to numbers
|
|
358
|
+
const num1 = parseInt(hash1);
|
|
359
|
+
const num2 = parseInt(hash2);
|
|
360
|
+
|
|
361
|
+
// Calculate normalized difference (0-1)
|
|
362
|
+
// Use a more conservative normalization factor
|
|
363
|
+
const diff = Math.abs(num1 - num2) / (Math.pow(2, 30) - 1);
|
|
364
|
+
return Math.min(1, diff);
|
|
365
|
+
} catch (e) {
|
|
366
|
+
console.error('Error calculating hash difference:', e);
|
|
367
|
+
return 1;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// Detect CSS3D animation loops
|
|
372
|
+
const detectCSS3DAnimationLoop = (frames, currentHash) => {
|
|
373
|
+
// Need at least 30 frames to detect a loop (increased from 20)
|
|
374
|
+
if (frames.length < 30) {
|
|
375
|
+
return { loopDetected: false, loopSize: 0 };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Use a much more conservative threshold for CSS3D
|
|
379
|
+
const minLoopSize = 6; // Increased minimum loop size (was 4)
|
|
380
|
+
const maxLoopSize = Math.floor(frames.length / 3); // Reduced from frames.length/2
|
|
381
|
+
const loopThreshold = 0.2; // Much more conservative threshold (was 0.1)
|
|
382
|
+
|
|
383
|
+
// Try different loop sizes
|
|
384
|
+
for (let loopSize = minLoopSize; loopSize <= maxLoopSize; loopSize++) {
|
|
385
|
+
let isLoop = true;
|
|
386
|
+
let matchScore = 0;
|
|
387
|
+
|
|
388
|
+
// Compare the last loopSize frames with the previous loopSize frames
|
|
389
|
+
for (let i = 0; i < loopSize; i++) {
|
|
390
|
+
const currentIndex = frames.length - 1 - i;
|
|
391
|
+
const previousIndex = currentIndex - loopSize;
|
|
392
|
+
|
|
393
|
+
if (previousIndex < 0) {
|
|
394
|
+
isLoop = false;
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const currentFrameHash = i === 0 ? currentHash : frames[currentIndex].hash;
|
|
399
|
+
const previousFrameHash = frames[previousIndex].hash;
|
|
400
|
+
|
|
401
|
+
// Calculate difference
|
|
402
|
+
const diff = calculateDomHashDifference(currentFrameHash, previousFrameHash);
|
|
403
|
+
|
|
404
|
+
// If hashes are different by more than the threshold, it's not a loop
|
|
405
|
+
if (diff > loopThreshold) {
|
|
406
|
+
isLoop = false;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Track how close the match is
|
|
411
|
+
matchScore += (1 - diff);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Only consider it a loop if we have a good match score
|
|
415
|
+
const avgMatchScore = matchScore / loopSize;
|
|
416
|
+
if (isLoop && avgMatchScore > 0.7) { // Require at least 70% match confidence
|
|
417
|
+
console.log(`Detected CSS3D animation loop of ${loopSize} frames with match score ${avgMatchScore.toFixed(2)}`);
|
|
418
|
+
return { loopDetected: true, loopSize, matchScore: avgMatchScore };
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return { loopDetected: false, loopSize: 0, matchScore: 0 };
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// Function to analyze CSS3D animation state
|
|
426
|
+
const analyzeCSS3DAnimation = (iframe, domSnapshotFrames, currentHash) => {
|
|
427
|
+
try {
|
|
428
|
+
if (!iframe || !iframe.contentWindow || !iframe.contentWindow.__css3dAnimationDetection) {
|
|
429
|
+
return {
|
|
430
|
+
isAnimating: false,
|
|
431
|
+
metrics: {}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const detection = iframe.contentWindow.__css3dAnimationDetection;
|
|
436
|
+
const now = Date.now();
|
|
437
|
+
|
|
438
|
+
// Check if there are active animations
|
|
439
|
+
const hasActiveTimeouts = detection.activeTimeouts > 0;
|
|
440
|
+
const hasActiveIntervals = detection.activeIntervals > 0;
|
|
441
|
+
const hasActiveRAF = detection.rAF > 0 && detection.animationFrameIds.size > 0;
|
|
442
|
+
const hasCssAnimations = detection.cssAnimations && detection.cssAnimations.size > 0;
|
|
443
|
+
const hasCssTransitions = detection.cssTransitions && detection.cssTransitions.size > 0;
|
|
444
|
+
|
|
445
|
+
// Check for recent DOM changes
|
|
446
|
+
const timeSinceLastDomChange = now - (detection.lastDomChange || 0);
|
|
447
|
+
const hasRecentDomChanges = timeSinceLastDomChange < 500; // Consider DOM changes in last 500ms as active
|
|
448
|
+
|
|
449
|
+
// Determine if animation is active
|
|
450
|
+
const isAnimating = hasActiveTimeouts || hasActiveIntervals || hasActiveRAF ||
|
|
451
|
+
hasCssAnimations || hasCssTransitions || hasRecentDomChanges;
|
|
452
|
+
|
|
453
|
+
// Check for loop patterns in DOM snapshots
|
|
454
|
+
let loopDetected = false;
|
|
455
|
+
let loopSize = 0;
|
|
456
|
+
let matchScore = 0;
|
|
457
|
+
|
|
458
|
+
// Only try to detect loops if we have enough frames and animation seems to be happening
|
|
459
|
+
if (domSnapshotFrames.length >= 30 && (isAnimating || detection.domChanges > 10)) {
|
|
460
|
+
const result = detectCSS3DAnimationLoop(domSnapshotFrames, currentHash);
|
|
461
|
+
loopDetected = result.loopDetected;
|
|
462
|
+
loopSize = result.loopSize;
|
|
463
|
+
matchScore = result.matchScore;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return {
|
|
467
|
+
isAnimating,
|
|
468
|
+
loopDetected,
|
|
469
|
+
loopSize,
|
|
470
|
+
matchScore,
|
|
471
|
+
metrics: {
|
|
472
|
+
activeTimeouts: detection.activeTimeouts,
|
|
473
|
+
activeIntervals: detection.activeIntervals,
|
|
474
|
+
rAF: detection.rAF,
|
|
475
|
+
cssAnimations: detection.cssAnimations ? detection.cssAnimations.size : 0,
|
|
476
|
+
cssTransitions: detection.cssTransitions ? detection.cssTransitions.size : 0,
|
|
477
|
+
domChanges: detection.domChanges,
|
|
478
|
+
timeSinceLastDomChange,
|
|
479
|
+
matchScore
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
} catch (e) {
|
|
483
|
+
console.error('Error analyzing CSS3D animation:', e);
|
|
484
|
+
return {
|
|
485
|
+
isAnimating: false,
|
|
486
|
+
metrics: {}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// Function to capture DOM snapshots until animation completes or times out
|
|
492
|
+
const captureDomSnapshots = async () => {
|
|
493
|
+
if (!isPreviewActive || !preRenderingInProgress) {
|
|
494
|
+
setPreRenderingInProgress(false);
|
|
495
|
+
startFinalProgressAnimation();
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const now = Date.now();
|
|
500
|
+
|
|
501
|
+
// Check if enough time has passed since last capture
|
|
502
|
+
// This ensures we capture at a similar rate to image2texture
|
|
503
|
+
const timeSinceLastCapture = now - lastCaptureTime;
|
|
504
|
+
if (timeSinceLastCapture < captureInterval) {
|
|
505
|
+
// Schedule next check
|
|
506
|
+
requestAnimationFrame(captureDomSnapshots);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Update last capture time
|
|
511
|
+
lastCaptureTime = now;
|
|
512
|
+
|
|
513
|
+
// Update progress based on more accurate metrics
|
|
514
|
+
if (now - lastProgressUpdate > progressUpdateInterval) {
|
|
515
|
+
lastProgressUpdate = now;
|
|
516
|
+
|
|
517
|
+
// Calculate elapsed time percentage
|
|
518
|
+
const elapsedTime = now - preRenderStartTime;
|
|
519
|
+
const timeProgress = Math.min(90, (elapsedTime / preRenderMaxDuration) * 100);
|
|
520
|
+
|
|
521
|
+
// Calculate frame-based progress
|
|
522
|
+
let frameProgress = Math.min(90, (domSnapshotFrames.length / totalFramesEstimate) * 100);
|
|
523
|
+
|
|
524
|
+
// Use a weighted combination of time and frame progress
|
|
525
|
+
// Cap at maxProgressBeforeFinalAnimation to leave room for final animation
|
|
526
|
+
const combinedProgress = Math.min(
|
|
527
|
+
maxProgressBeforeFinalAnimation,
|
|
528
|
+
(timeProgress * 0.3) + (frameProgress * 0.7)
|
|
529
|
+
);
|
|
530
|
+
updateProgress(combinedProgress);
|
|
531
|
+
|
|
532
|
+
// Update the loading text to show more information
|
|
533
|
+
const progressText = document.getElementById('loading-progress-text');
|
|
534
|
+
if (progressText) {
|
|
535
|
+
progressText.textContent = `Analyzing CSS3D animation... ${domSnapshotFrames.length} snapshots captured`;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Inject animation detection script if not already done
|
|
540
|
+
if (domSnapshotFrames.length === 0) {
|
|
541
|
+
injectUnifiedAnimationDetectionScript(iframe, 'css3d');
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Create a DOM snapshot
|
|
545
|
+
const snapshot = createDomSnapshot(iframe);
|
|
546
|
+
|
|
547
|
+
if (snapshot) {
|
|
548
|
+
// Add snapshot to frames
|
|
549
|
+
domSnapshotFrames.push({
|
|
550
|
+
...snapshot,
|
|
551
|
+
timestamp: now
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// Only start analyzing after we have enough frames
|
|
555
|
+
// Use same threshold as image2texture (20 frames)
|
|
556
|
+
if (domSnapshotFrames.length >= 20) {
|
|
557
|
+
// Analyze the CSS3D animation
|
|
558
|
+
const analysis = analyzeCSS3DAnimation(
|
|
559
|
+
iframe,
|
|
560
|
+
domSnapshotFrames.slice(0, -1),
|
|
561
|
+
snapshot.hash
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
// Store analysis metrics
|
|
565
|
+
analysisMetrics = analysis.metrics;
|
|
566
|
+
|
|
567
|
+
// Check if we've detected a loop with high confidence
|
|
568
|
+
if (analysis.loopDetected && analysis.matchScore > 0.7) {
|
|
569
|
+
console.log('CSS3D animation loop detected after ' + domSnapshotFrames.length + ' snapshots with match score ' + analysis.matchScore.toFixed(2));
|
|
570
|
+
setPreRenderingInProgress(false);
|
|
571
|
+
setIsAnimationFinite(true);
|
|
572
|
+
setAnimationDuration(domSnapshotFrames[domSnapshotFrames.length - 1].timestamp - domSnapshotFrames[0].timestamp);
|
|
573
|
+
|
|
574
|
+
// Update analysis metrics
|
|
575
|
+
loopDetected = true;
|
|
576
|
+
detectedLoopSize = analysis.loopSize;
|
|
577
|
+
|
|
578
|
+
// Show success message
|
|
579
|
+
if (isLongExposureMode) {
|
|
580
|
+
showStatus(`CSS3D animation loop detected, creating long exposure from ${domSnapshotFrames.length} frames`, 'info');
|
|
581
|
+
} else {
|
|
582
|
+
showStatus(`CSS3D animation loop detected (${(animationDuration/1000).toFixed(1)}s), ${domSnapshotFrames.length} snapshots captured`, 'success');
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Start final progress animation
|
|
586
|
+
startFinalProgressAnimation();
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Check if animation has ended (no changes for a while)
|
|
591
|
+
if (domSnapshotFrames.length > 20 && !analysis.isAnimating) {
|
|
592
|
+
// Check if there have been no significant changes in the last few frames
|
|
593
|
+
let noChanges = true;
|
|
594
|
+
const recentFrames = domSnapshotFrames.slice(-5);
|
|
595
|
+
|
|
596
|
+
for (let i = 1; i < recentFrames.length; i++) {
|
|
597
|
+
const diff = calculateDomHashDifference(recentFrames[i].hash, recentFrames[i-1].hash);
|
|
598
|
+
if (diff > 0.01) { // Small threshold for changes
|
|
599
|
+
noChanges = false;
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (noChanges) {
|
|
605
|
+
console.log('CSS3D animation end detected after ' + domSnapshotFrames.length + ' snapshots');
|
|
606
|
+
setPreRenderingInProgress(false);
|
|
607
|
+
setIsAnimationFinite(true);
|
|
608
|
+
setAnimationDuration(domSnapshotFrames[domSnapshotFrames.length - 1].timestamp - domSnapshotFrames[0].timestamp);
|
|
609
|
+
|
|
610
|
+
// Update analysis metrics
|
|
611
|
+
endDetected = true;
|
|
612
|
+
|
|
613
|
+
// Show success message
|
|
614
|
+
showStatus(`CSS3D animation end detected (${(animationDuration/1000).toFixed(1)}s), ${domSnapshotFrames.length} snapshots captured`, 'success');
|
|
615
|
+
|
|
616
|
+
// Start final progress animation
|
|
617
|
+
startFinalProgressAnimation();
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Check if we've exceeded the maximum pre-rendering time
|
|
625
|
+
if (now - preRenderStartTime > preRenderMaxDuration) {
|
|
626
|
+
console.log('CSS3D analysis time limit reached after ' + preRenderMaxDuration + 'ms');
|
|
627
|
+
setPreRenderingInProgress(false);
|
|
628
|
+
|
|
629
|
+
const analysis = analyzeCSS3DAnimation(iframe, domSnapshotFrames, null);
|
|
630
|
+
|
|
631
|
+
// Store final analysis metrics
|
|
632
|
+
analysisMetrics = analysis.metrics;
|
|
633
|
+
|
|
634
|
+
if (analysis.loopDetected) {
|
|
635
|
+
setIsAnimationFinite(true);
|
|
636
|
+
setAnimationDuration(domSnapshotFrames[domSnapshotFrames.length - 1].timestamp - domSnapshotFrames[0].timestamp);
|
|
637
|
+
console.log(`CSS3D animation loop detected, duration: ${animationDuration}ms, ${domSnapshotFrames.length} snapshots captured`);
|
|
638
|
+
showStatus(`CSS3D animation loop detected (${(animationDuration/1000).toFixed(1)}s), ${domSnapshotFrames.length} snapshots captured`, 'success');
|
|
639
|
+
|
|
640
|
+
// Update analysis metrics
|
|
641
|
+
loopDetected = true;
|
|
642
|
+
detectedLoopSize = analysis.loopSize;
|
|
643
|
+
} else if (!analysis.isAnimating) {
|
|
644
|
+
setIsAnimationFinite(true);
|
|
645
|
+
setAnimationDuration(domSnapshotFrames[domSnapshotFrames.length - 1].timestamp - domSnapshotFrames[0].timestamp);
|
|
646
|
+
console.log(`CSS3D animation appears to have ended, duration: ${animationDuration}ms, ${domSnapshotFrames.length} snapshots captured`);
|
|
647
|
+
showStatus(`CSS3D animation end detected (${(animationDuration/1000).toFixed(1)}s), ${domSnapshotFrames.length} snapshots captured`, 'success');
|
|
648
|
+
|
|
649
|
+
// Update analysis metrics
|
|
650
|
+
endDetected = true;
|
|
651
|
+
} else {
|
|
652
|
+
console.log(`No CSS3D animation end detected, ${domSnapshotFrames.length} snapshots captured`);
|
|
653
|
+
showStatus(`CSS3D animation appears infinite, ${domSnapshotFrames.length} snapshots captured for playback`, 'info');
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Start final progress animation
|
|
657
|
+
startFinalProgressAnimation();
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Continue capturing snapshots with requestAnimationFrame
|
|
662
|
+
// The timing is controlled by the captureInterval check at the beginning
|
|
663
|
+
requestAnimationFrame(captureDomSnapshots);
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
// Start capturing DOM snapshots
|
|
667
|
+
captureDomSnapshots();
|
|
668
|
+
|
|
669
|
+
// Store callback to be called after final animation completes
|
|
670
|
+
window._preRenderCallback = callback;
|
|
671
|
+
|
|
672
|
+
// Store the DOM snapshot frames in the preRenderedFrames array for compatibility
|
|
673
|
+
setPreRenderedFrames(domSnapshotFrames);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Initialize playback timing - called when preview starts playing
|
|
678
|
+
* This should be called regardless of animation type (finite/infinite)
|
|
679
|
+
*/
|
|
680
|
+
function initializePlaybackTiming() {
|
|
681
|
+
const now = Date.now();
|
|
682
|
+
setAnimationPlaybackStartTime(now);
|
|
683
|
+
|
|
684
|
+
// Calculate the capture start time from the first frame if available
|
|
685
|
+
if (preRenderedFrames.length > 0) {
|
|
686
|
+
setAnimationCaptureStartTime(preRenderedFrames[0].timestamp);
|
|
687
|
+
} else {
|
|
688
|
+
setAnimationCaptureStartTime(now);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
console.log('Playback timing initialized:', {
|
|
692
|
+
playbackStart: animationPlaybackStartTime,
|
|
693
|
+
captureStart: animationCaptureStartTime,
|
|
694
|
+
framesAvailable: preRenderedFrames.length
|
|
695
|
+
});
|
|
696
|
+
}
|