@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
|
File without changes
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { injectUnifiedAnimationDetectionScript } from '../../data/animation-classifier';
|
|
3
|
+
import { loadHtml2Canvas } from '../../loaders/html2canvas-loader';
|
|
4
|
+
|
|
5
|
+
const HTML2CANVAS_DEBUG_FLAG = false;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a texture from the iframe content using html2canvas
|
|
9
|
+
* @param {HTMLIFrameElement} iframe - The iframe containing the HTML content
|
|
10
|
+
* @returns {Promise<THREE.Texture>} A promise that resolves to a Three.js texture
|
|
11
|
+
*/
|
|
12
|
+
export async function createTextureFromIframe(iframe) {
|
|
13
|
+
return new Promise(async (resolve, reject) => {
|
|
14
|
+
try {
|
|
15
|
+
|
|
16
|
+
// Make sure we can access the iframe
|
|
17
|
+
if (!iframe || !document.body.contains(iframe)) {
|
|
18
|
+
reject(new Error('Iframe not found in DOM or removed'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Ensure html2canvas is loaded
|
|
23
|
+
const html2canvasAvailable = await loadHtml2Canvas();
|
|
24
|
+
if (!html2canvasAvailable) {
|
|
25
|
+
reject(new Error('html2canvas library not available - cannot render HTML to texture'));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Create a simple delay function
|
|
30
|
+
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
31
|
+
|
|
32
|
+
// Give more time for the iframe to fully load - increase from 300ms to 1000ms
|
|
33
|
+
delay(1000).then(async () => {
|
|
34
|
+
try {
|
|
35
|
+
// Check if we can access the iframe content safely
|
|
36
|
+
if (!iframe.contentDocument || !iframe.contentWindow) {
|
|
37
|
+
reject(new Error('Cannot access iframe content - security restriction or iframe removed'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Make sure the body is fully loaded
|
|
41
|
+
if (!iframe.contentDocument.body) {
|
|
42
|
+
reject(new Error('Iframe body not available - iframe content failed to load'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Inject animation detection script if not already injected
|
|
47
|
+
if (!iframe.contentWindow.__animationDetection) {
|
|
48
|
+
injectUnifiedAnimationDetectionScript(iframe, 'image2texture');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Ensure iframe is visible for capture (even if off-screen)
|
|
52
|
+
const originalStyle = iframe.style.cssText;
|
|
53
|
+
iframe.style.position = 'absolute';
|
|
54
|
+
iframe.style.left = '-9999px';
|
|
55
|
+
iframe.style.visibility = 'visible';
|
|
56
|
+
iframe.style.opacity = '1';
|
|
57
|
+
|
|
58
|
+
// Apply a frame to the content to make it more visible on the texture
|
|
59
|
+
const styleElement = iframe.contentDocument.createElement('style');
|
|
60
|
+
|
|
61
|
+
// Never show borders when capturing for long exposure
|
|
62
|
+
const shouldShowBorders = window.showPreviewBorders;
|
|
63
|
+
|
|
64
|
+
styleElement.textContent = `
|
|
65
|
+
body {
|
|
66
|
+
margin: 0;
|
|
67
|
+
padding: 15px;
|
|
68
|
+
${shouldShowBorders ? 'border: 5px solid #3498db;' : ''}
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
background-color: white !important; /* Force white background */
|
|
71
|
+
font-size: 20px !important; /* Increase base font size for better readability */
|
|
72
|
+
min-height: 100vh;
|
|
73
|
+
width: 100%;
|
|
74
|
+
overflow: auto;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Add a subtle grid to help with alignment */
|
|
78
|
+
body::before {
|
|
79
|
+
content: "";
|
|
80
|
+
position: absolute;
|
|
81
|
+
top: 0;
|
|
82
|
+
left: 0;
|
|
83
|
+
right: 0;
|
|
84
|
+
bottom: 0;
|
|
85
|
+
background-image: ${shouldShowBorders ?
|
|
86
|
+
'linear-gradient(rgba(0,0,0,0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,0.03) 1px, transparent 1px)' :
|
|
87
|
+
'none'};
|
|
88
|
+
background-size: 20px 20px;
|
|
89
|
+
pointer-events: none;
|
|
90
|
+
z-index: -1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Increase font size of common elements for better readability */
|
|
94
|
+
h1, h2, h3, h4, h5, h6 {
|
|
95
|
+
font-size: 1.5em !important;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
p, div, span, a, li, td, th {
|
|
99
|
+
font-size: 1.2em !important;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Make sure buttons and inputs are readable */
|
|
103
|
+
button, input, select, textarea {
|
|
104
|
+
font-size: 1.2em !important;
|
|
105
|
+
padding: 5px !important;
|
|
106
|
+
background-color: #fff;
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
// Add the style element temporarily for rendering
|
|
112
|
+
iframe.contentDocument.head.appendChild(styleElement);
|
|
113
|
+
|
|
114
|
+
// Force a layout/repaint in the iframe
|
|
115
|
+
iframe.contentWindow.scrollTo(0, 0);
|
|
116
|
+
|
|
117
|
+
// Wait a bit longer for styles to apply
|
|
118
|
+
await delay(200);
|
|
119
|
+
|
|
120
|
+
// Use html2canvas to capture the iframe content
|
|
121
|
+
const targetElement = iframe.contentDocument.body;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// Increase scale factor for better quality
|
|
125
|
+
const canvas = await window.html2canvas(targetElement, {
|
|
126
|
+
backgroundColor: '#FFFFFF', // Explicitly set to white to match HTML default
|
|
127
|
+
scale: 8, // Significantly increased from 4 for higher resolution textures
|
|
128
|
+
logging: HTML2CANVAS_DEBUG_FLAG, // Enable logging
|
|
129
|
+
allowTaint: true,
|
|
130
|
+
useCORS: true,
|
|
131
|
+
foreignObjectRendering: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Remove the temporary style element after rendering
|
|
135
|
+
if (styleElement && styleElement.parentNode) {
|
|
136
|
+
styleElement.parentNode.removeChild(styleElement);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Restore original iframe style
|
|
140
|
+
iframe.style.cssText = originalStyle;
|
|
141
|
+
|
|
142
|
+
// Check if canvas has content (not a blank capture)
|
|
143
|
+
const ctx = canvas.getContext('2d');
|
|
144
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
145
|
+
const data = imageData.data;
|
|
146
|
+
|
|
147
|
+
// Check if the canvas is entirely white/transparent
|
|
148
|
+
let hasContent = false;
|
|
149
|
+
let nonWhitePixelCount = 0;
|
|
150
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
151
|
+
// If any pixel has non-white color or non-zero alpha, there's content
|
|
152
|
+
if (data[i] < 255 || data[i+1] < 255 || data[i+2] < 255 || data[i+3] > 0) {
|
|
153
|
+
hasContent = true;
|
|
154
|
+
nonWhitePixelCount++;
|
|
155
|
+
if (nonWhitePixelCount > 100) break; // No need to count all of them
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!hasContent) {
|
|
160
|
+
reject(new Error('Canvas capture appears to be blank - no content was rendered'));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Create texture from the canvas
|
|
165
|
+
const texture = new THREE.CanvasTexture(canvas);
|
|
166
|
+
|
|
167
|
+
// Improve texture quality settings
|
|
168
|
+
texture.anisotropy = 16; // Doubled from 8 for better quality at angles
|
|
169
|
+
texture.minFilter = THREE.LinearFilter;
|
|
170
|
+
texture.magFilter = THREE.LinearFilter;
|
|
171
|
+
texture.generateMipmaps = false;
|
|
172
|
+
texture.needsUpdate = true;
|
|
173
|
+
|
|
174
|
+
resolve(texture);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
|
|
177
|
+
// Remove the temporary style element if it exists
|
|
178
|
+
if (styleElement && styleElement.parentNode) {
|
|
179
|
+
styleElement.parentNode.removeChild(styleElement);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Restore original iframe style
|
|
183
|
+
iframe.style.cssText = originalStyle;
|
|
184
|
+
|
|
185
|
+
// Properly reject with error
|
|
186
|
+
reject(new Error(`Failed to capture HTML content with html2canvas: ${error.message}`));
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
reject(new Error(`Error creating texture from iframe: ${error.message}`));
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
reject(new Error(`Error in iframe content processing: ${error.message}`));
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
reject(new Error(`Failed to create texture from iframe: ${error.message}`));
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import {
|
|
3
|
+
animationDetected,
|
|
4
|
+
animationDetectionSensitivity,
|
|
5
|
+
animationDuration,
|
|
6
|
+
animationStartDetected,
|
|
7
|
+
finalProgressAnimation,
|
|
8
|
+
finalProgressDuration,
|
|
9
|
+
finalProgressStartTime,
|
|
10
|
+
isAnimationFinite,
|
|
11
|
+
isPreviewActive,
|
|
12
|
+
preRenderedFrames,
|
|
13
|
+
preRenderingInProgress,
|
|
14
|
+
preRenderMaxDuration,
|
|
15
|
+
setAnimationDuration,
|
|
16
|
+
setFinalProgressAnimation,
|
|
17
|
+
setFinalProgressStartTime,
|
|
18
|
+
setIsAnimationFinite,
|
|
19
|
+
setIsPreviewAnimationPaused,
|
|
20
|
+
setPreRenderedFrames,
|
|
21
|
+
setPreRenderingInProgress,
|
|
22
|
+
} from "../../state/animation-state";
|
|
23
|
+
import { showStatus } from '../../../modals/html-editor-modal/html-editor-modal';
|
|
24
|
+
import { createMeshInfoPanel } from '../../../widgets/mesh-info-widget';
|
|
25
|
+
import { logAnimationAnalysisReport } from '../../state/log-util';
|
|
26
|
+
import { startPlayback, updateMeshTexture } from '../playback/animation-playback-controller';
|
|
27
|
+
import { createTextureFromIframe } from './iframe2texture-render-controller';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Start pre-rendering animation frames
|
|
31
|
+
* @param {HTMLIFrameElement} iframe - The iframe containing the HTML 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 {CustomTextureSettings} settings - Optional settings object for texture configuration
|
|
35
|
+
* @param {THREE.Mesh} previewPlane - The mesh to apply textures to
|
|
36
|
+
*/
|
|
37
|
+
export function startImage2TexturePreRendering(iframe, callback, progressBar = null, settings = null, previewPlane = null) {
|
|
38
|
+
if (!iframe) {
|
|
39
|
+
console.error('No iframe provided for pre-rendering');
|
|
40
|
+
if (callback) callback();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Reset state
|
|
45
|
+
setPreRenderingInProgress(true);
|
|
46
|
+
setPreRenderedFrames([]);
|
|
47
|
+
|
|
48
|
+
// Set the start time
|
|
49
|
+
const preRenderStartTime = Date.now();
|
|
50
|
+
|
|
51
|
+
// Track progress metrics
|
|
52
|
+
let totalFramesEstimate = 120; // Initial estimate
|
|
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
|
+
// Track animation detection variables
|
|
60
|
+
let loopDetected = false;
|
|
61
|
+
let endDetected = false;
|
|
62
|
+
let analysisMetrics = {};
|
|
63
|
+
|
|
64
|
+
// Get animation settings from passed settings object instead of DOM
|
|
65
|
+
let isLongExposureMode = false;
|
|
66
|
+
let playbackSpeed = 1.0;
|
|
67
|
+
|
|
68
|
+
if (settings) {
|
|
69
|
+
// Use settings parameters instead of DOM elements
|
|
70
|
+
isLongExposureMode = settings.isLongExposureMode;
|
|
71
|
+
playbackSpeed = settings.playbackSpeed || 1.0;
|
|
72
|
+
} else {
|
|
73
|
+
// Fallback to DOM access if settings not provided (for backward compatibility)
|
|
74
|
+
const animationTypeSelect = document.getElementById('html-animation-type');
|
|
75
|
+
isLongExposureMode = animationTypeSelect && animationTypeSelect.value === 'longExposure';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Set flag if we're capturing for long exposure
|
|
79
|
+
if (isLongExposureMode) {
|
|
80
|
+
setCapturingForLongExposure(true);
|
|
81
|
+
|
|
82
|
+
// Temporarily disable borders during capture
|
|
83
|
+
const originalBorderSetting = window.showPreviewBorders;
|
|
84
|
+
window.showPreviewBorders = false;
|
|
85
|
+
console.log('Borders temporarily disabled for long exposure capture');
|
|
86
|
+
|
|
87
|
+
// Store original setting to restore later
|
|
88
|
+
window._originalBorderSetting = originalBorderSetting;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Function to update progress bar
|
|
92
|
+
const updateProgress = (percent) => {
|
|
93
|
+
if (progressBar) {
|
|
94
|
+
// Ensure progress never exceeds maxProgressBeforeFinalAnimation unless in final animation
|
|
95
|
+
if (!finalProgressAnimation && percent > maxProgressBeforeFinalAnimation) {
|
|
96
|
+
percent = maxProgressBeforeFinalAnimation;
|
|
97
|
+
}
|
|
98
|
+
progressBar.style.width = `${percent}%`;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Function to create the long exposure texture and apply it
|
|
103
|
+
const createAndApplyLongExposure = () => {
|
|
104
|
+
if (preRenderedFrames.length > 0) {
|
|
105
|
+
// Use the playbackSpeed from settings instead of DOM
|
|
106
|
+
const longExposureTexture = createLongExposureTexture(preRenderedFrames, playbackSpeed);
|
|
107
|
+
|
|
108
|
+
// Update the mesh with the long exposure texture
|
|
109
|
+
if (previewPlane) {
|
|
110
|
+
updateMeshTexture(longExposureTexture, previewPlane);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Show a message about the long exposure
|
|
114
|
+
showStatus(`Long exposure created from ${preRenderedFrames.length} frames`, 'success');
|
|
115
|
+
|
|
116
|
+
// Pause animation since we just want to display the static image
|
|
117
|
+
setIsPreviewAnimationPaused(true);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Function to start final progress animation
|
|
122
|
+
const startFinalProgressAnimation = () => {
|
|
123
|
+
if (finalProgressAnimation) return; // Already animating
|
|
124
|
+
|
|
125
|
+
setFinalProgressAnimation(true);
|
|
126
|
+
setFinalProgressStartTime(Date.now());
|
|
127
|
+
|
|
128
|
+
// Start the animation loop
|
|
129
|
+
animateFinalProgress();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Function to animate progress to 100%
|
|
133
|
+
const animateFinalProgress = () => {
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
const elapsed = now - finalProgressStartTime;
|
|
136
|
+
|
|
137
|
+
if (elapsed >= finalProgressDuration) {
|
|
138
|
+
// Animation complete, set to 100%
|
|
139
|
+
updateProgress(100);
|
|
140
|
+
|
|
141
|
+
// Log animation analysis report
|
|
142
|
+
logAnimationAnalysisReport('Image2Texture', {
|
|
143
|
+
frameCount: preRenderedFrames.length,
|
|
144
|
+
duration: animationDuration,
|
|
145
|
+
isFinite: isAnimationFinite,
|
|
146
|
+
loopDetected,
|
|
147
|
+
endDetected,
|
|
148
|
+
analysisTime: now - preRenderStartTime,
|
|
149
|
+
metrics: analysisMetrics
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Hide loading overlay with fade out
|
|
153
|
+
const loadingOverlay = document.getElementById('pre-rendering-overlay');
|
|
154
|
+
if (loadingOverlay) {
|
|
155
|
+
loadingOverlay.style.transition = 'opacity 0.5s ease';
|
|
156
|
+
loadingOverlay.style.opacity = '0';
|
|
157
|
+
|
|
158
|
+
// Remove after fade out
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
if (loadingOverlay.parentNode) {
|
|
161
|
+
loadingOverlay.parentNode.removeChild(loadingOverlay);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Now create the info panel after pre-rendering is complete
|
|
165
|
+
const canvasContainer = document.querySelector('#html-preview-content');
|
|
166
|
+
if (canvasContainer) {
|
|
167
|
+
const modal = document.getElementById('html-editor-modal');
|
|
168
|
+
const currentMeshId = parseInt(modal.dataset.meshId);
|
|
169
|
+
createMeshInfoPanel(canvasContainer, currentMeshId);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// For long exposure, create the static image now that all frames are captured
|
|
173
|
+
if (isLongExposureMode && preRenderedFrames.length > 0) {
|
|
174
|
+
// Use playbackSpeed from settings instead of DOM
|
|
175
|
+
const longExposureTexture = createLongExposureTexture(preRenderedFrames, playbackSpeed);
|
|
176
|
+
|
|
177
|
+
// Update the mesh with the long exposure texture
|
|
178
|
+
if (previewPlane) {
|
|
179
|
+
updateMeshTexture(longExposureTexture, previewPlane);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Show a message about the long exposure
|
|
183
|
+
showStatus(`Long exposure created from ${preRenderedFrames.length} frames`, 'success');
|
|
184
|
+
|
|
185
|
+
// Pause animation since we just want to display the static image
|
|
186
|
+
setIsPreviewAnimationPaused(true);
|
|
187
|
+
} else {
|
|
188
|
+
// Reset animation start time to now
|
|
189
|
+
startPlayback();
|
|
190
|
+
|
|
191
|
+
// Start the animation
|
|
192
|
+
setIsPreviewAnimationPaused(false);
|
|
193
|
+
|
|
194
|
+
// Show a message that playback is starting
|
|
195
|
+
showStatus(`Animation playback starting at ${playbackSpeed}x speed`, 'success');
|
|
196
|
+
}
|
|
197
|
+
}, 500);
|
|
198
|
+
|
|
199
|
+
// Don't continue the animation
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
// Calculate progress based on easing function
|
|
204
|
+
const progress = easeOutCubic(elapsed / finalProgressDuration);
|
|
205
|
+
const currentProgress = maxProgressBeforeFinalAnimation + (100 - maxProgressBeforeFinalAnimation) * progress;
|
|
206
|
+
updateProgress(currentProgress);
|
|
207
|
+
|
|
208
|
+
// Update loading text
|
|
209
|
+
const progressText = document.getElementById('loading-progress-text');
|
|
210
|
+
if (progressText) {
|
|
211
|
+
if (isLongExposureMode) {
|
|
212
|
+
progressText.textContent = 'Creating long exposure...';
|
|
213
|
+
} else {
|
|
214
|
+
progressText.textContent = 'Finalizing animation...';
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Continue animation
|
|
219
|
+
requestAnimationFrame(animateFinalProgress);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Easing function for smooth animation
|
|
224
|
+
const easeOutCubic = (x) => {
|
|
225
|
+
return 1 - Math.pow(1 - x, 3);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Create high-quality texture from iframe for better visuals
|
|
229
|
+
const createHighQualityTexture = async (iframe) => {
|
|
230
|
+
try {
|
|
231
|
+
const texture = await createTextureFromIframe(iframe);
|
|
232
|
+
|
|
233
|
+
// Apply higher quality settings
|
|
234
|
+
texture.anisotropy = 16; // Increased from 8 for sharper textures
|
|
235
|
+
texture.minFilter = THREE.LinearFilter;
|
|
236
|
+
texture.magFilter = THREE.LinearFilter;
|
|
237
|
+
texture.generateMipmaps = false;
|
|
238
|
+
texture.needsUpdate = true;
|
|
239
|
+
|
|
240
|
+
return texture;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.error('Error creating high-quality texture:', error);
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Function to capture frames until animation completes or times out
|
|
248
|
+
const captureFrames = async () => {
|
|
249
|
+
if (!isPreviewActive || !preRenderingInProgress) {
|
|
250
|
+
setPreRenderingInProgress(false);
|
|
251
|
+
startFinalProgressAnimation();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const now = Date.now();
|
|
256
|
+
|
|
257
|
+
// Update progress based on more accurate metrics
|
|
258
|
+
if (now - lastProgressUpdate > progressUpdateInterval) {
|
|
259
|
+
lastProgressUpdate = now;
|
|
260
|
+
|
|
261
|
+
// Calculate elapsed time percentage
|
|
262
|
+
const elapsedTime = now - preRenderStartTime;
|
|
263
|
+
const timeProgress = Math.min(90, (elapsedTime / preRenderMaxDuration) * 100);
|
|
264
|
+
|
|
265
|
+
// Calculate frame-based progress
|
|
266
|
+
let frameProgress = 0;
|
|
267
|
+
if (animationDetected) {
|
|
268
|
+
// If we've detected animation, adjust the total frames estimate
|
|
269
|
+
if (preRenderedFrames.length > totalFramesEstimate * 0.5) {
|
|
270
|
+
// If we've captured more than half our estimate, update the estimate
|
|
271
|
+
totalFramesEstimate = Math.max(totalFramesEstimate, Math.ceil(preRenderedFrames.length * 1.2));
|
|
272
|
+
}
|
|
273
|
+
frameProgress = Math.min(90, (preRenderedFrames.length / totalFramesEstimate) * 100);
|
|
274
|
+
} else {
|
|
275
|
+
// If no animation detected, use time-based progress
|
|
276
|
+
frameProgress = timeProgress;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Use a weighted combination of time and frame progress
|
|
280
|
+
// Cap at maxProgressBeforeFinalAnimation to leave room for final animation
|
|
281
|
+
const combinedProgress = Math.min(
|
|
282
|
+
maxProgressBeforeFinalAnimation,
|
|
283
|
+
(timeProgress * 0.3) + (frameProgress * 0.7)
|
|
284
|
+
);
|
|
285
|
+
updateProgress(combinedProgress);
|
|
286
|
+
|
|
287
|
+
// Update the loading text to show more information
|
|
288
|
+
const progressText = document.getElementById('loading-progress-text');
|
|
289
|
+
if (progressText) {
|
|
290
|
+
progressText.textContent = `Pre-rendering animation... ${preRenderedFrames.length} frames captured`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Check if we've detected a loop and have enough frames
|
|
295
|
+
// Use the improved detection logic with higher sensitivity for pre-rendering
|
|
296
|
+
const sensitivity = animationDetectionSensitivity + 0.1; // Slight boost during pre-rendering
|
|
297
|
+
const isLoopDetected = preRenderedFrames.length > 20 &&
|
|
298
|
+
detectAnimationLoop(preRenderedFrames, sensitivity);
|
|
299
|
+
|
|
300
|
+
if (isLoopDetected && preRenderedFrames.length > 20) {
|
|
301
|
+
console.log('Animation loop detected after ' + preRenderedFrames.length + ' frames');
|
|
302
|
+
setPreRenderingInProgress(false);
|
|
303
|
+
setIsAnimationFinite(true);
|
|
304
|
+
setAnimationDuration(preRenderedFrames[preRenderedFrames.length - 1].timestamp - preRenderedFrames[0].timestamp);
|
|
305
|
+
|
|
306
|
+
// Update analysis metrics
|
|
307
|
+
loopDetected = true;
|
|
308
|
+
|
|
309
|
+
// Show success message
|
|
310
|
+
if (isLongExposureMode) {
|
|
311
|
+
showStatus(`Animation loop detected, creating long exposure from ${preRenderedFrames.length} frames`, 'info');
|
|
312
|
+
} else {
|
|
313
|
+
showStatus(`Animation loop detected (${(animationDuration/1000).toFixed(1)}s), ${preRenderedFrames.length} frames captured`, 'success');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Start final progress animation instead of immediately calling callback
|
|
317
|
+
startFinalProgressAnimation();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Check for animation end using the new analysis function
|
|
322
|
+
if (preRenderedFrames.length > 20) { // Reduced from 30
|
|
323
|
+
// Calculate the latest frame hash
|
|
324
|
+
const latestFrameHash = preRenderedFrames[preRenderedFrames.length - 1].hash;
|
|
325
|
+
|
|
326
|
+
// Analyze frames to detect animation end with higher sensitivity
|
|
327
|
+
const analysisResult = analyzeAnimationFrames(
|
|
328
|
+
preRenderedFrames.slice(0, -1), // All frames except the latest
|
|
329
|
+
latestFrameHash,
|
|
330
|
+
sensitivity
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// Store analysis metrics
|
|
334
|
+
analysisMetrics = analysisResult.metrics;
|
|
335
|
+
|
|
336
|
+
// If end detected and we have enough frames, stop pre-rendering
|
|
337
|
+
if (analysisResult.endDetected && preRenderedFrames.length > 20) {
|
|
338
|
+
console.log('Animation end detected during pre-rendering after ' + preRenderedFrames.length + ' frames');
|
|
339
|
+
console.log('Detection metrics:', analysisResult.metrics);
|
|
340
|
+
setPreRenderingInProgress(false);
|
|
341
|
+
setIsAnimationFinite(true);
|
|
342
|
+
setAnimationDuration(preRenderedFrames[preRenderedFrames.length - 1].timestamp - preRenderedFrames[0].timestamp);
|
|
343
|
+
|
|
344
|
+
// Update analysis metrics
|
|
345
|
+
endDetected = true;
|
|
346
|
+
|
|
347
|
+
// Show success message
|
|
348
|
+
showStatus(`Animation end detected (${(animationDuration/1000).toFixed(1)}s), ${preRenderedFrames.length} frames captured`, 'success');
|
|
349
|
+
|
|
350
|
+
// Start final progress animation instead of immediately calling callback
|
|
351
|
+
startFinalProgressAnimation();
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Check if we've exceeded the maximum pre-rendering time
|
|
357
|
+
if (now - preRenderStartTime > preRenderMaxDuration) {
|
|
358
|
+
console.log('Pre-rendering time limit reached after ' + preRenderMaxDuration + 'ms');
|
|
359
|
+
setPreRenderingInProgress(false);
|
|
360
|
+
|
|
361
|
+
if (loopDetected) {
|
|
362
|
+
setIsAnimationFinite(true);
|
|
363
|
+
setAnimationDuration(preRenderedFrames[preRenderedFrames.length - 1].timestamp - preRenderedFrames[0].timestamp);
|
|
364
|
+
console.log(`Animation loop detected, duration: ${animationDuration}ms, ${preRenderedFrames.length} frames captured`);
|
|
365
|
+
showStatus(`Animation loop detected (${(animationDuration/1000).toFixed(1)}s), ${preRenderedFrames.length} frames captured`, 'success');
|
|
366
|
+
} else if (animationStartDetected) {
|
|
367
|
+
setIsAnimationFinite(true);
|
|
368
|
+
setAnimationDuration(animationEndTime - animationStartTime);
|
|
369
|
+
console.log(`Animation start/end detected, duration: ${animationDuration}ms, ${preRenderedFrames.length} frames captured`);
|
|
370
|
+
showStatus(`Animation start/end detected (${(animationDuration/1000).toFixed(1)}s), ${preRenderedFrames.length} frames captured`, 'success');
|
|
371
|
+
|
|
372
|
+
// Update analysis metrics
|
|
373
|
+
endDetected = true;
|
|
374
|
+
} else {
|
|
375
|
+
console.log(`No animation loop detected, ${preRenderedFrames.length} frames captured`);
|
|
376
|
+
showStatus(`Animation appears infinite, ${preRenderedFrames.length} frames captured for playback`, 'info');
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Start final progress animation instead of immediately calling callback
|
|
380
|
+
startFinalProgressAnimation();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
// Capture a high-quality frame
|
|
386
|
+
const texture = await createHighQualityTexture(iframe);
|
|
387
|
+
|
|
388
|
+
// Calculate a hash of the texture to detect changes
|
|
389
|
+
const frameHash = calculateTextureHash(texture);
|
|
390
|
+
|
|
391
|
+
// Add frame to pre-rendered frames
|
|
392
|
+
preRenderedFrames.push({
|
|
393
|
+
texture: texture,
|
|
394
|
+
timestamp: now,
|
|
395
|
+
hash: frameHash
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// For long exposure mode, if we have enough frames, create the texture immediately
|
|
399
|
+
// This prevents showing the first frame before the long exposure
|
|
400
|
+
if (isLongExposureMode && preRenderedFrames.length >= 15) {
|
|
401
|
+
// Create the long exposure immediately
|
|
402
|
+
createAndApplyLongExposure();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Use a shorter delay for more frequent frame capture to increase smoothness
|
|
406
|
+
setTimeout(() => {
|
|
407
|
+
requestAnimationFrame(captureFrames);
|
|
408
|
+
}, 5); // 5ms delay allows for more frames to be captured in the same time
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error('Error during pre-rendering:', error);
|
|
411
|
+
setPreRenderingInProgress(false);
|
|
412
|
+
|
|
413
|
+
// Start final progress animation instead of immediately calling callback
|
|
414
|
+
startFinalProgressAnimation();
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// Start capturing frames
|
|
419
|
+
captureFrames();
|
|
420
|
+
|
|
421
|
+
// Store callback to be called after final animation completes
|
|
422
|
+
window._preRenderCallback = callback;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Calculate a simple hash of a texture to detect changes between frames
|
|
427
|
+
* @param {THREE.Texture} texture - The texture to hash
|
|
428
|
+
* @returns {string} A simple hash of the texture
|
|
429
|
+
*/
|
|
430
|
+
function calculateTextureHash(texture) {
|
|
431
|
+
if (!texture || !texture.image) return '';
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
// Create a small canvas to sample the texture
|
|
435
|
+
const canvas = document.createElement('canvas');
|
|
436
|
+
const size = 16; // Small sample size for performance
|
|
437
|
+
canvas.width = size;
|
|
438
|
+
canvas.height = size;
|
|
439
|
+
const ctx = canvas.getContext('2d');
|
|
440
|
+
|
|
441
|
+
// Draw the texture to the canvas
|
|
442
|
+
ctx.drawImage(texture.image, 0, 0, size, size);
|
|
443
|
+
|
|
444
|
+
// Get image data
|
|
445
|
+
const imageData = ctx.getImageData(0, 0, size, size).data;
|
|
446
|
+
|
|
447
|
+
// Sample pixels at regular intervals
|
|
448
|
+
const samples = [];
|
|
449
|
+
const step = 4 * 4; // Sample every 4th pixel (RGBA)
|
|
450
|
+
for (let i = 0; i < imageData.length; i += step) {
|
|
451
|
+
// Use just the RGB values (skip alpha)
|
|
452
|
+
samples.push(imageData[i], imageData[i+1], imageData[i+2]);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Create a simple hash from the samples
|
|
456
|
+
return samples.join(',');
|
|
457
|
+
} catch (e) {
|
|
458
|
+
console.error('Error calculating texture hash:', e);
|
|
459
|
+
return '';
|
|
460
|
+
}
|
|
461
|
+
}
|