(geometry, material)
| 496 | wireframeLines.material.resolution.set( |
| 497 | w * renderer.getPixelRatio(), |
| 498 | h * renderer.getPixelRatio(), |
| 499 | ); |
| 500 | } |
| 501 | requestRender(); |
| 502 | } |
| 503 | |
| 504 | function disposeGroup(group) { |
| 505 | group.traverse(obj => { |
| 506 | if (obj.geometry) obj.geometry.dispose(); |
| 507 | if (obj.material) { |
| 508 | if (Array.isArray(obj.material)) { |
| 509 | obj.material.forEach(m => { if (m.map) m.map.dispose(); m.dispose(); }); |
| 510 | } else { |
| 511 | if (obj.material.map) obj.material.map.dispose(); |
| 512 | obj.material.dispose(); |
| 513 | } |
| 514 | } |
| 515 | }); |
| 516 | } |
| 517 | |
| 518 | /** |
| 519 | * Replace the mesh in the scene with new geometry. |
| 520 | * @param {THREE.BufferGeometry} geometry |
| 521 | * @param {THREE.Material} [material] – if omitted, a default material is used |
| 522 | */ |
| 523 | export function loadGeometry(geometry, material) { |
| 524 | // Clear previous mesh |
| 525 | while (meshGroup.children.length) { |
| 526 | const old = meshGroup.children[0]; |
| 527 | old.geometry.dispose(); |
| 528 | if (old.material && old.material.dispose) old.material.dispose(); |
| 529 | meshGroup.remove(old); |
| 530 | } |
| 531 | |
| 532 | const mat = material || new THREE.MeshStandardMaterial({ |
| 533 | color: 0xaaaacc, |
| 534 | roughness: 0.6, |
| 535 | metalness: 0.1, |
| 536 | side: THREE.DoubleSide, |
| 537 | }); |
| 538 | |
| 539 | if (!geometry.attributes.normal) geometry.computeVertexNormals(); |
| 540 | |
| 541 | currentMesh = new THREE.Mesh(geometry, mat); |
| 542 | currentMesh.castShadow = true; |
| 543 | currentMesh.receiveShadow = true; |
| 544 | meshGroup.add(currentMesh); |
| 545 | |
| 546 | // Rebuild wireframe overlay to match the new geometry |
| 547 | // (old overlay is already gone because meshGroup was cleared above) |
| 548 | wireframeLines = null; |
| 549 | if (wireframeVisible) _buildWireframe(geometry); |
| 550 | |
| 551 | // Position grid at mesh bottom (Z-up: move grid along Z) |
| 552 | geometry.computeBoundingBox(); |
| 553 | const box = geometry.boundingBox; |
| 554 | const groundZ = box.min.z - 0.01; |
| 555 | grid.position.z = groundZ; |
no test coverage detected