@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.
Files changed (114) hide show
  1. package/bin/cli.js +69 -0
  2. package/package.json +13 -7
  3. package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
  4. package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
  5. package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
  6. package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
  7. package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
  8. package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
  9. package/src/asset_debugger/header/header.css +73 -0
  10. package/src/asset_debugger/header/header.html +24 -0
  11. package/src/asset_debugger/header/header.js +224 -0
  12. package/src/asset_debugger/index.html +76 -0
  13. package/src/asset_debugger/landing-page/landing-page.css +396 -0
  14. package/src/asset_debugger/landing-page/landing-page.html +81 -0
  15. package/src/asset_debugger/landing-page/landing-page.js +611 -0
  16. package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
  17. package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
  18. package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
  19. package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
  20. package/src/asset_debugger/main.css +14 -0
  21. package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
  22. package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
  23. package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
  24. package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
  25. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
  26. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
  27. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
  28. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
  29. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
  30. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
  31. package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
  32. package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
  33. package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
  34. package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
  35. package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
  36. package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
  37. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
  38. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
  39. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
  40. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
  41. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
  42. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
  43. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
  44. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
  45. package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
  46. package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
  47. package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
  48. package/src/asset_debugger/router.js +190 -0
  49. package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
  50. package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
  51. package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
  52. package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
  53. package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
  54. package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
  55. package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
  56. package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
  57. package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
  58. package/src/asset_debugger/util/common.css +280 -0
  59. package/src/asset_debugger/util/data/animation-classifier.js +323 -0
  60. package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
  61. package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
  62. package/src/asset_debugger/util/data/glb-classifier.js +290 -0
  63. package/src/asset_debugger/util/data/html-formatter.js +76 -0
  64. package/src/asset_debugger/util/data/html-linter.js +276 -0
  65. package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
  66. package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
  67. package/src/asset_debugger/util/data/string-serder.js +303 -0
  68. package/src/asset_debugger/util/data/texture-classifier.js +663 -0
  69. package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
  70. package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
  71. package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
  72. package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
  73. package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
  74. package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
  75. package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
  76. package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
  77. package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
  78. package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
  79. package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
  80. package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
  81. package/src/asset_debugger/util/rig/rig-controller.js +612 -0
  82. package/src/asset_debugger/util/rig/rig-factory.js +628 -0
  83. package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
  84. package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
  85. package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
  86. package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
  87. package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
  88. package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
  89. package/src/asset_debugger/util/scene/background-manager.js +284 -0
  90. package/src/asset_debugger/util/scene/camera-controller.js +243 -0
  91. package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
  92. package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
  93. package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
  94. package/src/asset_debugger/util/scene/glb-controller.js +208 -0
  95. package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
  96. package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
  97. package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
  98. package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
  99. package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
  100. package/src/asset_debugger/util/scene/ui-manager.js +107 -0
  101. package/src/asset_debugger/util/state/animation-state.js +128 -0
  102. package/src/asset_debugger/util/state/css3d-state.js +83 -0
  103. package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
  104. package/src/asset_debugger/util/state/log-util.js +197 -0
  105. package/src/asset_debugger/util/state/scene-state.js +452 -0
  106. package/src/asset_debugger/util/state/threejs-state.js +54 -0
  107. package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
  108. package/src/asset_debugger/util/workers/model-worker.js +109 -0
  109. package/src/asset_debugger/util/workers/texture-worker.js +54 -0
  110. package/src/asset_debugger/util/workers/worker-manager.js +212 -0
  111. package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
  112. package/src/index.html +261 -0
  113. package/src/index.js +8 -0
  114. package/vite.config.js +66 -0
@@ -0,0 +1,190 @@
1
+ import { showLoadingSplash, updateLoadingProgress, hideLoadingSplash } from './loading-splash/loading-splash';
2
+
3
+ class Router {
4
+ constructor() {
5
+ this.routes = new Map();
6
+ this.appDiv = document.getElementById('app');
7
+ this.currentModuleCleanup = null;
8
+ this.init();
9
+ }
10
+
11
+ addRoute(path, handler) {
12
+ this.routes.set(path, handler);
13
+ return this;
14
+ }
15
+
16
+ init() {
17
+ this.isHandlingRoute = false;
18
+
19
+ window.addEventListener('hashchange', this.handleRouteDebounced.bind(this));
20
+ window.addEventListener('load', this.handleRouteDebounced.bind(this));
21
+
22
+ if (!window.location.hash) {
23
+ this.navigate('/landing');
24
+ }
25
+ }
26
+
27
+ async handleRouteDebounced() {
28
+ if (this.isHandlingRoute) {
29
+ return;
30
+ }
31
+
32
+ this.isHandlingRoute = true;
33
+ try {
34
+ await this.handleRoute();
35
+ } finally {
36
+ this.isHandlingRoute = false;
37
+ }
38
+ }
39
+
40
+ async handleRoute() {
41
+ const hash = window.location.hash.slice(1) || '/landing';
42
+ const handler = this.routes.get(hash);
43
+
44
+ if (handler) {
45
+ await handler();
46
+ } else {
47
+ this.navigate('/landing');
48
+ }
49
+ }
50
+
51
+ navigate(path) {
52
+ window.location.hash = path;
53
+ }
54
+
55
+ navigateToPage(targetPage, params = {}) {
56
+ const navigationEvent = new CustomEvent('routerNavigation', {
57
+ detail: { targetPage, params, timestamp: Date.now() }
58
+ });
59
+ window.dispatchEvent(navigationEvent);
60
+
61
+ const routeMap = {
62
+ 'debugger-scene': '/debugger-scene',
63
+ 'landing': '/landing',
64
+ 'tools': '/tools'
65
+ };
66
+
67
+ const route = routeMap[targetPage];
68
+ if (route) {
69
+ this.navigate(route);
70
+ } else {
71
+ this.navigate('/landing');
72
+ }
73
+ }
74
+
75
+ async clearContent() {
76
+ if (this.currentModuleCleanup) {
77
+ try {
78
+ const result = this.currentModuleCleanup();
79
+ this.currentModuleCleanup = null;
80
+ } catch (error) {
81
+ this.currentModuleCleanup = null;
82
+ }
83
+ }
84
+
85
+ this.cleanupCSS3DElements();
86
+ this.appDiv.innerHTML = '';
87
+ }
88
+
89
+ cleanupCSS3DElements() {
90
+ const css3dRenderers = document.querySelectorAll('div[style*="position: absolute"][style*="z-index: 1000"]');
91
+ css3dRenderers.forEach(renderer => {
92
+ if (renderer.parentNode && renderer !== this.appDiv) {
93
+ renderer.parentNode.removeChild(renderer);
94
+ }
95
+ });
96
+
97
+ const css3dIframes = document.querySelectorAll('iframe[style*="border: 2px solid #00ff88"]');
98
+ css3dIframes.forEach(iframe => {
99
+ if (iframe.parentNode) {
100
+ iframe.parentNode.removeChild(iframe);
101
+ }
102
+ });
103
+
104
+ const css3dElements = document.querySelectorAll('[style*="pointer-events: none"][style*="position: absolute"]');
105
+ css3dElements.forEach(element => {
106
+ if (element.parentNode && element !== this.appDiv && element.style.zIndex === '1000') {
107
+ element.parentNode.removeChild(element);
108
+ }
109
+ });
110
+ }
111
+
112
+ async loadContent(url) {
113
+ await this.clearContent();
114
+ await this.ensureHeaderLoaded();
115
+
116
+ const response = await fetch(url);
117
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
118
+
119
+ const html = await response.text();
120
+ const contentToInsert = this.extractContent(html, url);
121
+ this.appDiv.innerHTML = contentToInsert;
122
+
123
+ await new Promise(resolve => requestAnimationFrame(resolve));
124
+ }
125
+
126
+ extractContent(html, url) {
127
+ if (!html.includes('<!DOCTYPE html>')) {
128
+ return html;
129
+ }
130
+
131
+ const parser = new DOMParser();
132
+ const doc = parser.parseFromString(html, 'text/html');
133
+ const body = doc.body.cloneNode(true);
134
+
135
+ const elementsToRemove = [
136
+ '#header-container',
137
+ 'script',
138
+ 'link[rel="stylesheet"]'
139
+ ];
140
+
141
+ elementsToRemove.forEach(selector => {
142
+ const elements = body.querySelectorAll(selector);
143
+ elements.forEach(el => el.remove());
144
+ });
145
+
146
+ return body.innerHTML;
147
+ }
148
+
149
+ async ensureHeaderLoaded() {
150
+ const headerContainer = document.getElementById('header-container');
151
+ if (headerContainer && headerContainer.innerHTML.trim() === '') {
152
+ try {
153
+ const { loadHeader } = await import('./header/header.js');
154
+ await loadHeader('header-container');
155
+ } catch (error) {
156
+ // Header loading failed
157
+ }
158
+ }
159
+ }
160
+
161
+ async initializeModule(modulePath, initFunctionName, routeName) {
162
+ try {
163
+ const module = await import(/* @vite-ignore */ `${modulePath}?t=${Date.now()}`);
164
+
165
+ if (module[initFunctionName]) {
166
+ const cleanup = await module[initFunctionName]();
167
+
168
+ if (cleanup && typeof cleanup === 'function') {
169
+ this.currentModuleCleanup = cleanup;
170
+ }
171
+ }
172
+ } catch (error) {
173
+ // Module initialization failed
174
+ }
175
+ }
176
+ }
177
+
178
+ const router = new Router();
179
+
180
+ router
181
+ .addRoute('/landing', async () => {
182
+ await router.loadContent('./landing-page/landing-page.html');
183
+ await router.initializeModule('./landing-page/landing-page.js', 'initalizeLandingPage', 'landing page');
184
+ })
185
+ .addRoute('/debugger-scene', async () => {
186
+ await router.loadContent('./debugger-scene/debugger-scene.html');
187
+ await router.initializeModule('./debugger-scene/debugger-scene.js', 'setupDebuggerScene', 'debugger scene');
188
+ });
189
+
190
+ window.appRouter = router;
@@ -0,0 +1,150 @@
1
+ import { frameInterval } from "./animation-preview-controller";
2
+ import {
3
+ ANALYSIS_DURATION_MS,
4
+ animationDuration,
5
+ isAnimationFinite,
6
+ isPlaybackActive,
7
+ playbackStartTime,
8
+ preRenderedFrames,
9
+ setIsPlaybackActive,
10
+ setIsPreviewAnimationPaused,
11
+ setPlaybackStartTime
12
+ } from "../../state/animation-state";
13
+ import { setLastAnimationTime } from "../../state/css3d-state";
14
+
15
+ /**
16
+ * Start playback timing - called when preview should begin playing
17
+ */
18
+ export function startPlayback() {
19
+ setPlaybackStartTime(Date.now());
20
+ setIsPlaybackActive(true);
21
+ console.log('Playback started at:', playbackStartTime);
22
+ }
23
+
24
+ /**
25
+ * Stop playback timing
26
+ */
27
+ export function stopPlayback() {
28
+ setIsPlaybackActive(false);
29
+ console.log('Playback stopped');
30
+ }
31
+
32
+ /**
33
+ * Get current frame based on elapsed playback time
34
+ */
35
+ export function getCurrentFrameForPlayback(playbackSpeed = 1.0, animationType = 'play') {
36
+ if (!isPlaybackActive || preRenderedFrames.length === 0) {
37
+ return preRenderedFrames.length > 0 ? preRenderedFrames[preRenderedFrames.length - 1] : null;
38
+ }
39
+
40
+ const now = Date.now();
41
+ const playbackElapsed = now - playbackStartTime;
42
+ const adjustedElapsed = playbackElapsed * playbackSpeed;
43
+
44
+ // Use the analysis duration as our animation duration
45
+ // This represents the full time period we analyzed for animation
46
+ const naturalDuration = isAnimationFinite && animationDuration > 0 ?
47
+ animationDuration : ANALYSIS_DURATION_MS;
48
+
49
+ let normalizedTime;
50
+
51
+ switch (animationType) {
52
+ case 'play':
53
+ if (adjustedElapsed >= naturalDuration) {
54
+ setIsPreviewAnimationPaused(true);
55
+ return preRenderedFrames[preRenderedFrames.length - 1];
56
+ }
57
+ normalizedTime = adjustedElapsed / naturalDuration;
58
+ break;
59
+
60
+ case 'loop':
61
+ normalizedTime = (adjustedElapsed % naturalDuration) / naturalDuration;
62
+ break;
63
+
64
+ case 'bounce':
65
+ const cycle = Math.floor(adjustedElapsed / naturalDuration);
66
+ const position = (adjustedElapsed % naturalDuration) / naturalDuration;
67
+ normalizedTime = (cycle % 2 === 0) ? position : (1 - position);
68
+ break;
69
+
70
+ default:
71
+ normalizedTime = (adjustedElapsed % naturalDuration) / naturalDuration;
72
+ break;
73
+ }
74
+
75
+ const frameIndex = Math.min(
76
+ Math.floor(normalizedTime * preRenderedFrames.length),
77
+ preRenderedFrames.length - 1
78
+ );
79
+
80
+ return preRenderedFrames[frameIndex];
81
+ }
82
+
83
+
84
+
85
+ /**
86
+ * Update the mesh texture with the given texture
87
+ * @param {THREE.Texture} texture - The texture to apply to the mesh
88
+ * @param {THREE.Mesh} previewPlane - The mesh to update with the texture
89
+ */
90
+ export function updateMeshTexture(texture, previewPlane) {
91
+ if (!texture || !previewPlane || !previewPlane.material) return;
92
+
93
+ let needsUpdate = false;
94
+
95
+ if (Array.isArray(previewPlane.material)) {
96
+ previewPlane.material.forEach(material => {
97
+ if (material.map !== texture) {
98
+ material.map = texture;
99
+ needsUpdate = true;
100
+ }
101
+ });
102
+
103
+ if (needsUpdate) {
104
+ previewPlane.material.forEach(material => {
105
+ material.needsUpdate = true;
106
+ });
107
+ }
108
+ } else {
109
+ if (previewPlane.material.map !== texture) {
110
+ previewPlane.material.map = texture;
111
+ previewPlane.material.needsUpdate = true;
112
+ }
113
+ }
114
+ }
115
+
116
+ export function runAnimationFrame(renderer, scene, camera, mesh, settings, frameState) {
117
+ // Extract settings
118
+ const { animationType, playbackSpeed, isPaused } = settings;
119
+ const { lastFrameTime, frameInterval } = frameState;
120
+ // Frame rate throttling
121
+ const now = performance.now();
122
+ const elapsed = now - lastFrameTime;
123
+ if (elapsed < frameInterval) {
124
+ return null; // Return null to indicate no frame update
125
+ }
126
+ const newFrameTime = now - (elapsed % frameInterval);
127
+ // Skip frame updates if animation is paused
128
+ if (isPaused) {
129
+ // Still render the scene with the current frame
130
+ if (renderer && scene && camera) {
131
+ renderer.render(scene, camera);
132
+ }
133
+ return newFrameTime;
134
+ }
135
+ // Get current frame
136
+ const currentFrame = getCurrentFrameForPlayback(playbackSpeed, animationType);
137
+ // Update texture
138
+ if (currentFrame && currentFrame.texture && mesh) {
139
+ updateMeshTexture(currentFrame.texture, mesh);
140
+ }
141
+ // Apply mesh animation (this would need to be imported if moved to separate module)
142
+ if (animationType !== 'play' && !isPaused && mesh) {
143
+ applyMeshAnimation(animationType, playbackSpeed, mesh);
144
+ }
145
+ // Render scene
146
+ if (renderer && scene && camera) {
147
+ renderer.render(scene, camera);
148
+ }
149
+ return newFrameTime;
150
+ }
@@ -0,0 +1,316 @@
1
+ import * as THREE from 'three';
2
+ import { CustomTextureSettings, showStatus } from '../../../modals/html-editor-modal/html-editor-modal';
3
+ import {
4
+ resetPreRenderState,
5
+ setIsPreviewActive,
6
+ setIsPreviewAnimationPaused,
7
+ resetPlaybackTimingState,
8
+ isPreviewAnimationPaused,
9
+ isPreviewActive
10
+ } from '../../state/animation-state';
11
+ import { sanitizeHtml } from '../../data/string-serder';
12
+ import {
13
+ animationPreviewCamera,
14
+ animationPreviewRenderer,
15
+ animationPreviewScene,
16
+ previewPlane,
17
+ setPreviewRenderTarget
18
+ } from '../../state/threejs-state';
19
+ import { runAnimationFrame } from '../../animation/playback/animation-playback-controller';
20
+ import { setupCSS3DScene } from '../../scene/css3d-scene-manager';
21
+ import { startImage2TexturePreRendering } from '../../animation/render/image2texture-prerender-controller';
22
+ import { startCss3dPreRendering } from '../../animation/render/css3d-prerender-controller';
23
+ import { logError, logPreviewError } from '../../state/log-util';
24
+ import { cleanupThreeJsPreview, initThreeJsPreview } from '../../scene/threejs-preview-manager';
25
+
26
+ let currentPreviewSettings = null;
27
+ const targetFrameRate = 60;
28
+ let lastAnimationFrameTime = 0;
29
+ export const frameInterval = 1000 / targetFrameRate;
30
+ export let previewAnimationId = null;
31
+
32
+ // Core HTML rendering logic (reusable)
33
+ function createHtmlRenderer(html, onLoad) {
34
+ const sanitizedHtml = sanitizeHtml(html);
35
+
36
+ const renderIframe = document.createElement('iframe');
37
+ renderIframe.id = 'html-render-iframe';
38
+ renderIframe.style.width = '960px';
39
+ renderIframe.style.height = '540px';
40
+ renderIframe.style.position = 'absolute';
41
+ renderIframe.style.left = '-9999px';
42
+ renderIframe.style.top = '0';
43
+ renderIframe.style.border = 'none';
44
+ renderIframe.style.backgroundColor = 'transparent';
45
+ document.body.appendChild(renderIframe);
46
+
47
+ renderIframe.onload = onLoad;
48
+
49
+ try {
50
+ renderIframe.srcdoc = sanitizedHtml;
51
+ } catch (error) {
52
+ renderIframe.contentDocument.open();
53
+ renderIframe.contentDocument.write(sanitizedHtml);
54
+ renderIframe.contentDocument.close();
55
+ }
56
+
57
+ return renderIframe;
58
+ }
59
+
60
+ // Preview UI creation (preview-specific)
61
+ function createPreviewUI(previewContent) {
62
+ previewContent.style.position = 'relative';
63
+ previewContent.style.minHeight = '400px';
64
+ previewContent.style.height = '100%';
65
+
66
+ const canvasContainer = document.createElement('div');
67
+ canvasContainer.style.width = '100%';
68
+ canvasContainer.style.height = '100%';
69
+ canvasContainer.style.position = 'absolute';
70
+ canvasContainer.style.top = '0';
71
+ canvasContainer.style.left = '0';
72
+ canvasContainer.style.right = '0';
73
+ canvasContainer.style.bottom = '0';
74
+ canvasContainer.style.overflow = 'hidden';
75
+ canvasContainer.style.display = 'block';
76
+ previewContent.appendChild(canvasContainer);
77
+
78
+ const errorLog = document.createElement('div');
79
+ errorLog.id = 'html-preview-error-log';
80
+ errorLog.className = 'preview-error-log';
81
+ errorLog.style.display = 'none';
82
+ previewContent.appendChild(errorLog);
83
+
84
+ const loadingOverlay = createLoadingOverlay();
85
+ canvasContainer.appendChild(loadingOverlay);
86
+
87
+ return { canvasContainer, errorLog, loadingOverlay };
88
+ }
89
+
90
+ function createLoadingOverlay() {
91
+ const loadingOverlay = document.createElement('div');
92
+ loadingOverlay.id = 'pre-rendering-overlay';
93
+ loadingOverlay.className = 'loading-splash';
94
+ loadingOverlay.style.position = 'absolute';
95
+ loadingOverlay.style.top = '0';
96
+ loadingOverlay.style.left = '0';
97
+ loadingOverlay.style.width = '100%';
98
+ loadingOverlay.style.height = '100%';
99
+ loadingOverlay.style.backgroundColor = '#000000';
100
+ loadingOverlay.style.zIndex = '1000';
101
+ loadingOverlay.style.border = 'none';
102
+ loadingOverlay.style.outline = 'none';
103
+ loadingOverlay.style.boxShadow = 'none';
104
+
105
+ const loadingContent = document.createElement('div');
106
+ loadingContent.className = 'loading-content';
107
+ loadingContent.style.display = 'flex';
108
+ loadingContent.style.flexDirection = 'column';
109
+ loadingContent.style.alignItems = 'center';
110
+ loadingContent.style.justifyContent = 'center';
111
+ loadingContent.style.height = '100%';
112
+ loadingContent.style.width = '100%';
113
+ loadingContent.style.backgroundColor = '#000000';
114
+
115
+ const loadingTitle = document.createElement('h2');
116
+ loadingTitle.className = 'loading-title';
117
+ loadingTitle.textContent = 'PRE-RENDERING';
118
+ loadingTitle.style.color = 'white';
119
+ loadingTitle.style.margin = '0 0 20px 0';
120
+
121
+ const spinnerContainer = document.createElement('div');
122
+ spinnerContainer.className = 'loading-spinner-container';
123
+
124
+ const atomicSpinner = document.createElement('div');
125
+ atomicSpinner.className = 'atomic-spinner';
126
+
127
+ const nucleus = document.createElement('div');
128
+ nucleus.className = 'nucleus';
129
+ atomicSpinner.appendChild(nucleus);
130
+
131
+ for (let i = 0; i < 3; i++) {
132
+ const orbit = document.createElement('div');
133
+ orbit.className = 'electron-orbit';
134
+ const electron = document.createElement('div');
135
+ electron.className = 'electron';
136
+ orbit.appendChild(electron);
137
+ atomicSpinner.appendChild(orbit);
138
+ }
139
+
140
+ spinnerContainer.appendChild(atomicSpinner);
141
+
142
+ const progressText = document.createElement('div');
143
+ progressText.id = 'loading-progress-text';
144
+ progressText.className = 'loading-progress-text';
145
+ progressText.textContent = 'Pre-rendering animation...';
146
+ progressText.style.color = 'white';
147
+ progressText.style.marginTop = '20px';
148
+
149
+ const progressContainer = document.createElement('div');
150
+ progressContainer.style.width = '80%';
151
+ progressContainer.style.height = '4px';
152
+ progressContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.2)';
153
+ progressContainer.style.borderRadius = '2px';
154
+ progressContainer.style.overflow = 'hidden';
155
+ progressContainer.style.marginTop = '10px';
156
+
157
+ const progressBar = document.createElement('div');
158
+ progressBar.id = 'pre-rendering-progress';
159
+ progressBar.style.width = '0%';
160
+ progressBar.style.height = '100%';
161
+ progressBar.style.backgroundColor = '#3498db';
162
+ progressBar.style.transition = 'width 0.3s ease-out';
163
+
164
+ progressContainer.appendChild(progressBar);
165
+ loadingContent.appendChild(loadingTitle);
166
+ loadingContent.appendChild(spinnerContainer);
167
+ loadingContent.appendChild(progressText);
168
+ loadingContent.appendChild(progressContainer);
169
+ loadingOverlay.appendChild(loadingContent);
170
+
171
+ return loadingOverlay;
172
+ }
173
+
174
+ // Main preview function (wrapper)
175
+ export function initalizePreview(settings, previewContent, setModalData) {
176
+ if (!previewContent) return;
177
+
178
+ try {
179
+ const { html, meshId: currentMeshId, previewMode, playbackSpeed, animationType, isLongExposureMode, showPreviewBorders } = settings;
180
+
181
+ currentPreviewSettings = settings;
182
+ window.showPreviewBorders = showPreviewBorders;
183
+
184
+ resetPreRenderState();
185
+ resetPlaybackTimingState();
186
+
187
+ if (setModalData) {
188
+ setModalData('previewMode', previewMode);
189
+ }
190
+
191
+ cleanupThreeJsPreview();
192
+ previewContent.innerHTML = '';
193
+ setIsPreviewActive(true);
194
+
195
+ const statusMessage = isLongExposureMode
196
+ ? 'Pre-rendering animation for long exposure capture...'
197
+ : 'Pre-rendering animation for smooth playback...';
198
+ settings.updateStatus(statusMessage, 'info');
199
+
200
+ const renderIframe = createHtmlRenderer(html, () => {
201
+ if (!isPreviewActive) return;
202
+
203
+ const { canvasContainer, errorLog, loadingOverlay } = createPreviewUI(previewContent);
204
+ const progressBar = loadingOverlay.querySelector('#pre-rendering-progress');
205
+ const progressText = loadingOverlay.querySelector('#loading-progress-text');
206
+
207
+ setPreviewRenderTarget(renderIframe);
208
+ setIsPreviewAnimationPaused(true);
209
+
210
+ if (previewMode === 'css3d') {
211
+ console.log('CSS3D initialization will happen after pre-rendering');
212
+ } else {
213
+ if (settings && typeof settings.updateStatus === 'function') {
214
+ settings.updateStatus('Initializing 3D cube preview...', 'info');
215
+ } else {
216
+ showStatus('Initializing 3D cube preview...', 'info');
217
+ }
218
+ initThreeJsPreview(canvasContainer, renderIframe, currentMeshId, true);
219
+ }
220
+
221
+ switch (previewMode) {
222
+ case 'css3d':
223
+ progressText.textContent = 'Analyzing CSS3D animation...';
224
+ startCss3dPreRendering(renderIframe, () => {
225
+ settings.updateStatus('Initializing CSS3D preview...', 'info');
226
+ import('three/examples/jsm/renderers/CSS3DRenderer.js')
227
+ .then(module => {
228
+ const { CSS3DRenderer, CSS3DObject } = module;
229
+ setupCSS3DScene(canvasContainer, renderIframe, CSS3DRenderer, CSS3DObject, currentMeshId, true);
230
+ });
231
+ }, progressBar, settings, previewPlane);
232
+ break;
233
+
234
+ case 'threejs':
235
+ default:
236
+ progressText.textContent = 'Pre-rendering animation...';
237
+ startImage2TexturePreRendering(renderIframe, () => {
238
+ // Pre-rendering complete
239
+ }, progressBar, settings, previewPlane);
240
+ break;
241
+ }
242
+ });
243
+
244
+ } catch (error) {
245
+ logPreviewError(`Preview error: ${error.message}`, previewContent, settings.errorContainer, settings.statusCallback);
246
+ settings.handleError('Error generating preview: ' + error.message);
247
+ }
248
+ }
249
+
250
+
251
+ /**
252
+ * Preview-specific animation loop wrapper
253
+ */
254
+ export function animatePreview() {
255
+ // Preview-specific early exit check
256
+ if (!isPreviewActive) {
257
+ console.log('Preview no longer active, stopping animation loop');
258
+ return;
259
+ }
260
+
261
+ // Schedule next frame immediately for high priority
262
+ previewAnimationId = requestAnimationFrame(animatePreview);
263
+
264
+ try {
265
+ // Prepare preview-specific settings
266
+ const animationType = currentPreviewSettings?.animationType || 'play';
267
+ const playbackSpeed = currentPreviewSettings?.playbackSpeed || 1.0;
268
+
269
+ // Create settings object for generic animation function
270
+ const animationSettings = {
271
+ animationType,
272
+ playbackSpeed,
273
+ targetFrameRate,
274
+ isPaused: isPreviewAnimationPaused
275
+ };
276
+
277
+ // Create frame state object
278
+ const frameState = {
279
+ lastFrameTime: lastAnimationFrameTime,
280
+ frameInterval
281
+ };
282
+
283
+ // Call generic animation function
284
+ const newFrameTime = runAnimationFrame(
285
+ animationPreviewRenderer,
286
+ animationPreviewScene,
287
+ animationPreviewCamera,
288
+ previewPlane,
289
+ animationSettings,
290
+ frameState
291
+ );
292
+
293
+ // Update preview-specific frame time
294
+ if (newFrameTime !== null) {
295
+ lastAnimationFrameTime = newFrameTime;
296
+ }
297
+
298
+ } catch (error) {
299
+ console.error('Error in preview animation loop:', error);
300
+ }
301
+ }
302
+
303
+ export function resetPreviewAnimationId() {
304
+ previewAnimationId = null;
305
+ }
306
+
307
+ export function resetLastAnimationFrameTime() {
308
+ lastAnimationFrameTime = 0;
309
+ }
310
+
311
+ export function setLastAnimationFrameTime(incomingValue) {
312
+ if(!incomingValue) {
313
+ return;
314
+ }
315
+ lastAnimationFrameTime = incomingValue;
316
+ }