| 581 | if (!THREE.STLLoader) { |
| 582 | throw new Error('STLLoader not available'); |
| 583 | } |
| 584 | |
| 585 | const loader = new THREE.STLLoader(); |
| 586 | const arrayBuffer = await window.electron.readModelFile(filePath); |
| 587 | if (loadToken !== previewLoadToken) { |
| 588 | throw new Error('Preview cancelled'); |
| 589 | } |
| 590 | |
| 591 | validateSTLBuffer(arrayBuffer); |
| 592 | const geometry = loader.parse(arrayBuffer); |
| 593 | if (!geometry) { |
| 594 | throw new Error('Failed to parse STL geometry'); |
| 595 | } |
| 596 | |
| 597 | geometry.computeVertexNormals(); |
| 598 | geometry.computeBoundingBox(); |
| 599 | geometry.computeBoundingSphere(); |
| 600 | |
| 601 | const material = new THREE.MeshStandardMaterial({ |
| 602 | color: 0x4a9eff, |
| 603 | metalness: 0.3, |
| 604 | roughness: 0.6, |
| 605 | flatShading: false, |
| 606 | emissive: 0x002244, |
| 607 | emissiveIntensity: 0.2 |
| 608 | }); |
| 609 | |
| 610 | return new THREE.Mesh(geometry, material); |
| 611 | } |
| 612 | |
| 613 | const loading = document.getElementById('preview-loading'); |
| 614 | if (loading && loading.querySelector('p')) { |
| 615 | loading.querySelector('p').textContent = |
| 616 | 'Loading 3MF file...\nThis could take time for larger files.'; |
| 617 | } |
| 618 | |
| 619 | preview3mfRequestId = `${Date.now()}_${Math.random().toString(16).slice(2)}`; |
| 620 | const json = await window.electron.parse3MFPreview(filePath, preview3mfRequestId); |
| 621 | if (loadToken !== previewLoadToken) { |
| 622 | throw new Error('Preview cancelled'); |
| 623 | } |
| 624 | |
| 625 | if (!json) { |
| 626 | throw new Error('Failed to load 3MF file'); |
| 627 | } |
| 628 | |