当前位置: 首页 > news >正文

three-bvh-csg glb分割

复杂模型报错:

E:\project\3d_label\three-bvh-csg-main\examples\demo.html

<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>Stable GLB BVH CSG Cut</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.160.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/", "three-bvh-csg": "https://unpkg.com/three-bvh-csg@0.0.18/build/index.module.js" } } </script> <script type="module"> import * as THREE from "three"; import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { Brush, Evaluator, INTERSECTION, SUBTRACTION } from "three-bvh-csg"; /* ===================== 基础场景 ===================== */ const scene = new THREE.Scene(); scene.background = new THREE.Color(0x222222); const camera = new THREE.PerspectiveCamera(60, innerWidth/innerHeight, 0.1, 1000); camera.position.set(3, 3, 3); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(innerWidth, innerHeight); document.body.appendChild(renderer.domElement); const controls = new OrbitControls(camera, renderer.domElement); controls.update(); scene.add(new THREE.AmbientLight(0xffffff, 0.8)); const light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(5, 10, 5); scene.add(light); /* ===================== CSG ===================== */ const evaluator = new Evaluator(); /* ===================== GLB ===================== */ const loader = new GLTFLoader(); function cleanGeometry(mesh) { mesh.updateWorldMatrix(true, false); let geo = mesh.geometry.clone(); // ❗ 1. 转 world space geo.applyMatrix4(mesh.matrixWorld); // ❗ 2. 确保有 position attribute if (!geo.attributes || !geo.attributes.position) { console.warn("Invalid geometry skipped"); return null; } // ❗ 3. 转 indexed(关键) if (!geo.index) { geo = THREE.BufferGeometryUtils.mergeVertices(geo); } // ❗ 4. 清理 NaN / 冗余 geo.computeVertexNormals(); geo.computeBoundingBox(); geo.computeBoundingSphere(); return geo; } loader.load("yotown_clear.glb", (gltf) => { const model = gltf.scene; scene.add(model); model.updateMatrixWorld(true); /* ===================== 计算包围盒 ===================== */ const box = new THREE.Box3().setFromObject(model); const center = new THREE.Vector3(); const size = new THREE.Vector3(); box.getCenter(center); box.getSize(size); console.log("center:", center); console.log("size:", size); /* ===================== 创建切割平面 ===================== */ const planeGeo = new THREE.PlaneGeometry(200, 200); const planeMesh = new THREE.Mesh( planeGeo, new THREE.MeshBasicMaterial() ); planeMesh.position.copy(center); planeMesh.rotation.x = Math.PI / 2; // 水平切割(XZ 平面) /* ===================== 收集 meshes ===================== */ const meshes = []; model.traverse(o => { if (o.isMesh) { o.geometry.computeVertexNormals(); meshes.push(o); } }); const upperGroup = new THREE.Group(); const lowerGroup = new THREE.Group(); /* ===================== 执行切割 ===================== */ meshes.forEach(mesh => { const geometry = cleanGeometry(mesh); if (!geometry) return; const brushA = new Brush(geometry); /* ===== 切割体 ===== */ const upperPlane = new Brush(new THREE.BoxGeometry(1000, 1000, 1000)); upperPlane.position.set(center.x, center.y + 1000, center.z); upperPlane.updateMatrixWorld(true); const lowerPlane = new Brush(new THREE.BoxGeometry(1000, 1000, 1000)); lowerPlane.position.set(center.x, center.y - 1000, center.z); lowerPlane.updateMatrixWorld(true); /* ===== 上半 ===== */ const upper = evaluator.evaluate( brushA, upperPlane, INTERSECTION ); if (upper?.geometry?.attributes?.position) { upperGroup.add(new THREE.Mesh(upper.geometry, mesh.material.clone())); } /* ===== 下半 ===== */ const lower = evaluator.evaluate( brushA, lowerPlane, INTERSECTION ); if (lower?.geometry?.attributes?.position) { const m = new THREE.Mesh(lower.geometry, mesh.material.clone()); m.position.x += size.x * 1.2; lowerGroup.add(m); } }); scene.add(upperGroup); scene.add(lowerGroup); }); /* ===================== 渲染 ===================== */ function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); window.addEventListener("resize", () => { camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth, innerHeight); }); </script> </body> </html>
http://www.zskr.cn/news/1482308.html

相关文章:

  • 朝阳母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 白城母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 【CSDN AI服务退费白皮书】:基于137例真实退订案例的权威分析,含合同违约金计算公式
  • 终极免费开源项目管理方案:GanttProject完整使用指南
  • 书匠策AI官网www.shujiangce.com实测:期刊论文居然能像“搭乐高“一样拼出来?
  • ComfyUI-Manager高效配置实战指南:深度解析AI工作流管理最佳实践
  • Samba打印共享故障排查:禁用SPOOLSS协议解决CUPS连接被拒问题
  • 抖音内容采集与本地化管理的完整解决方案
  • Warcraft Helper终极指南:5分钟解决魔兽争霸III所有Win10/Win11兼容性问题
  • 微信小程序日历组件:5分钟打造专业级日期管理功能 [特殊字符]
  • Windows端口转发终极指南:3分钟学会图形化配置工具PortProxyGUI
  • 博客园 高性价比滤袋厂家 - 资讯纵览
  • 广州老房翻新多少钱?2026年各项目费用明细+避坑指南+公司推荐 - 优家闲谈
  • Windows任务栏透明化神器:3分钟让你的桌面焕然一新!
  • 书匠策AI官网www.shujiangce.com:你的期刊论文搭子,比导师还“懂行“
  • SteamAutoCrack:终极游戏DRM破解工具使用完全指南
  • 成都装修公司哪家好?2026年主流公司报价对比+怎么选 - 优家闲谈
  • 别再死磕公式了!用Python实战模拟TDOA定位(从Chan到Fang算法对比)
  • 5分钟快速掌握:NcmpGui免费极速NCM音乐转换终极方案
  • 昌邑母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 2026年6月不锈钢波纹管供应商哪家强,穿线软管/金属软管/电暖器/电热管/不锈钢波纹管,不锈钢波纹管工厂哪个好 - 品牌推荐师
  • Windows端口转发管理终极指南:如何用PortProxyGUI告别复杂命令
  • 3个关键步骤解锁Balena Etcher:跨平台系统镜像烧录新体验
  • 别只盯着SCI:如何高效搞定一次IEEE会议投稿(以CAC为例,含审稿状态解读与超页费避雷)
  • 区间本质不同子串数 题解
  • TegraRcmGUI:3分钟学会Switch RCM注入的Windows图形化工具
  • 2026 郑州黄金奢侈品回收:信任体系重构与标杆品牌价值 - 新闻快传
  • 掌握高效模组管理:XCOM 2 Alternative Mod Launcher完整实战指南
  • 3个步骤,用AcFunDown实现A站视频永久保存的完整指南
  • HarmonyOS厨房助手实战第5篇:JSON持久化、Repository分层与数据兼容