当前位置: 首页 > 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.jsqmd.com/news/970585/

相关文章:

  • 看完就会:2026年必不可少的专业AI论文平台
  • 微信聊天记录如何实现永久保存与智能分析?WeChatMsg工具深度解析
  • 朝阳母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 终极免费开源项目管理方案:GanttProject完整使用指南
  • 天津起名馆排名.天津起名老师推荐.天津起名大师推荐 - 资讯纵览
  • 白城母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • Blender终极曲线工具:Flexi Bézier完整使用指南
  • 从BBR到CUBIC:手把手教你用Jain‘s指数评估和对比主流TCP算法的公平性
  • 福州高三复读怎么选?老牌民办左海学校深耕复读赛道口碑出众,高考复读/私立初中/民办普高/高三复读,复读机构哪家靠谱 - 品牌推荐师
  • 【CSDN AI服务退费白皮书】:基于137例真实退订案例的权威分析,含合同违约金计算公式
  • 高级AMD Ryzen系统管理单元深度调试与性能优化配置指南
  • 书匠策AI官网www.shujiangce.com实测:期刊论文居然能像“搭乐高“一样拼出来?
  • 常德母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 深度解析!为什么亚马逊系统修改了我的 Listing 分类节点且销售排名消失! - 跨境电商卖家出海
  • 告别窗口遮挡烦恼:AlwaysOnTop让你的重要信息永远在最前面!
  • 赤水母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • Samba打印共享故障排查:禁用SPOOLSS协议解决CUPS连接被拒问题
  • 白银母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • ComfyUI-Manager高效配置实战指南:深度解析AI工作流管理最佳实践
  • 书匠策AI让你的期刊论文“开挂“?这个被90%研究生忽略的神器我必须安利!
  • 2026向量数据库技术选型:从Milvus到Pinecone,RAG场景下的性能对比与最佳实践:技术突破与开发实践全解析
  • 如何在Windows上实现完整的Switch控制器驱动:JoyCon-Driver技术深度解析
  • 抖音内容采集与本地化管理的完整解决方案
  • 亳州母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 深入理解HttpSecurity的设计
  • Warcraft Helper终极指南:5分钟解决魔兽争霸III所有Win10/Win11兼容性问题
  • 群晖NAS终极升级:Realtek USB以太网驱动完整实战指南
  • 博客园 高性价比滤袋厂家 - 资讯纵览
  • C#桌面程序入门实战:用Access做账号系统,带注册登录界面和完整VS工程
  • 百度网盘提取码智能获取:baidupankey如何实现3秒极速查询