javascript 3d网页 简单的 可视化编辑 图形界面 搭建几何体 带导出物体 示例 ( three.js r114 初探 六)
Posted weihexincode
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript 3d网页 简单的 可视化编辑 图形界面 搭建几何体 带导出物体 示例 ( three.js r114 初探 六)相关的知识,希望对你有一定的参考价值。
1 完整代码下载
https://pan.baidu.com/s/1JJyVcP2KqXsd5G6eaYpgHQ
提取码 3fzt (压缩包名: 2020-3-24-demo.zip)
2 图片展示
3 主要代码
1 "use strict" 2 3 ;(function (){ 4 5 //是否支持WebGL 6 if(WEBGL.isWebGLAvailable() === false){ 7 document.body.appendChild(WEBGL.getWebGLErrorMessage()); 8 return; 9 } 10 11 THREE.Cache.enabled = true;//加载器启用缓存 12 13 var _view = new View().initBody(); 14 var _width = _view.size.w; 15 var _height = _view.size.h; 16 var _minDistance = 0.1; 17 var _maxDistance = 5000; 18 19 //three 20 var _Three = function (){} 21 22 Object.assign(_Three.prototype, { 23 24 constructor: _Three, 25 26 depthMaterial: new THREE.MeshDepthMaterial({depthPacking: THREE.RGBADepthPacking}), 27 28 createScene: function (bg, fog){//场景 29 let scene = new THREE.Scene(); 30 if(typeof(bg) === "number"){scene.background = bg;} 31 else if(Array.isArray(bg) === true){ 32 scene.background = new THREE.CubeTextureLoader() 33 .setPath(‘img/cube/skyboxsun25deg/‘) 34 .load( [ ‘px.jpg‘, ‘nx.jpg‘, ‘py.jpg‘, ‘ny.jpg‘, ‘pz.jpg‘, ‘nz.jpg‘ ] ); 35 } 36 if(fog) scene.fog = new THREE.Fog(fog.color, fog.near, fog.far); 37 return scene; 38 }, 39 40 createCamera: function (fov, width, height, near, far){//相机 41 let w = width === undefined ? _width : width; 42 let h = height === undefined ? _height : height; 43 let camera = new THREE.PerspectiveCamera(fov || 45, w / h, near || _minDistance, far || _minDistance * 1000); 44 camera.position.set(0, 0, _maxDistance * 0.001); 45 camera.lookAt( 0, 0, 0 ); 46 return camera; 47 }, 48 49 createRenderer: function (felem, width, height, antialias, lights, shadow, checkShaderErrors){//渲染器 50 let fel = felem || document.body; 51 let renderer = new THREE.WebGLRenderer({ 52 antialias : antialias || false, //抗割齿 53 powerPreference:"high-performance" //选择高性能GPU渲染 54 }); 55 renderer.setSize(width || _width, height || _height); 56 renderer.setPixelRatio(window.devicePixelRatio); 57 renderer.gammaFactor = 2.2; //着色校正 58 renderer.physicallyCorrectLights = lights || false; //使其精确照明 59 renderer.shadowMap.enabled = shadow || false; //渲染阴影 60 renderer.debug.checkShaderErrors = checkShaderErrors || false; // 61 if(!renderer.extensions.get(‘WEBGL_depth_texture‘)){console.log("深度纹理扩展获取失败:WEBGL_depth_texture");} 62 fel.appendChild(renderer.domElement); 63 return renderer; 64 }, 65 66 addLinghts: function (scene, hc){//灯光 67 let a = new THREE.AmbientLight(0x696969);//环境光(无处不在的光,太阳光) 68 69 let b = new THREE.DirectionalLight(0xFFFFFF, 1);//平行光(产生阴影的光) 70 b.position.set(0, 50, 50);//位置 71 b.castShadow = true;// 产生动态阴影 72 //b.target = Object3D; //平行光的方向是从它的位置到目标位置。默认的目标位置为原点 (0,0,0)。 73 b.shadow.radius = 1;//使阴影边缘变模糊 74 b.shadow.bias = 0.0001; 75 b.shadow.mapSize.width = 1024;//阴影质量w 76 b.shadow.mapSize.height = 1024;//阴影质量h 77 78 //下面属性默认不自动更新 79 /* let l_d = 20; //光照区域的大小 100 * 100 80 b.shadow.camera.left = -l_d; 81 b.shadow.camera.right = l_d; 82 b.shadow.camera.top = l_d; 83 b.shadow.camera.bottom = -l_d; */ 84 b.shadow.camera.near = 0.1;//最近 85 b.shadow.camera.far = 100;//最远 86 87 88 /* var helperShadow = new THREE.CameraHelper(b.shadow.camera); 89 scene.add(a, b, helperShadow); 90 var c = ()=>{()=>{helperShadow.update(); hc.update(); }} 91 new Gui(b.position, ["x", "y", "z"], [-2500, 2500, 1]).change(c) 92 .add(b.shadow, "radius", [-1, 1, 0.1]).change(c) 93 .add(b.shadow.mapSize, ["width", "height"], [0, 2048, 1]).change(c) 94 .title("调试灯光");*/ 95 scene.add(a, b); 96 return [a, b]; 97 }, 98 99 addControls: function (scene, camera, renderer, children){//控件 100 101 //拖放控件 102 let drag = new THREE.DragControls(children, camera, renderer.domElement); 103 //drag.addEventListener(‘hoveron‘, (e)=>{this.addTarget(e.object);}); 104 drag.enabled = false; 105 //this.dragcontrols = dc; 106 107 //轨道控件 108 let orbit = new THREE.OrbitControls(camera, renderer.domElement); 109 orbit.target = new THREE.Vector3(0, 0, 0);//控件焦点 110 //orbit.minPolarAngle = Math.PI * 0.3;//向上最大角度 111 //orbit.maxPolarAngle = Math.PI * 0.4;//向下最大角度 112 orbit.minDistance = _minDistance;//最小距离 113 orbit.maxDistance = _maxDistance;//最大距离 114 orbit.autoRotateSpeed = 10;//自动旋转速度 115 //orbit.panSpeed = 100;//鼠标旋转速度 116 orbit.enableZoom = true;//是否启用缩放 117 orbit.enableKeys = true;//是否启用键盘 118 orbit.panSpeed = 1;//鼠标平移速度 119 orbit.keyPanSpeed = 100;//按键平移的速度 120 orbit.keys.LEFT = 65;//key a左 121 orbit.keys.UP = 87;//key w前 122 orbit.keys.RIGHT = 68;//key d右 123 orbit.keys.BOTTOM = 83;//key s后 124 orbit.addEventListener("change", ()=>{this.render(scene, camera, renderer);}); 125 126 127 //平移控件 128 var transform = new THREE.TransformControls(camera, renderer.domElement); 129 transform.addEventListener( ‘dragging-changed‘, (e)=>{orbit.enabled = !e.value;}); 130 transform.addEventListener(‘change‘, ()=>{this.render(scene, camera, renderer);}); 131 scene.add(transform); 132 133 return {drag: drag, orbit: orbit, transform: transform}; 134 }, 135 136 updateGeometry: function (mesh, geometry){//更换几何体 137 if(!mesh || !geometry){ 138 console.log("_Three: 几何体更换失败"); 139 return; 140 } 141 mesh.geometry.dispose(); 142 mesh.geometry = geometry; 143 }, 144 145 updateMaterial: function (mesh, material){//更换材质 146 if(!mesh || !material){ 147 console.log("_Three: 材质更换失败"); 148 return; 149 } 150 let newMat = material.clone(true); 151 //newMat.color.copy(mesh.material.color); 152 newMat.transparent = true; 153 if(mesh.material.map !== null){this.updateTexture(newMat, mesh.material.map);} 154 mesh.material.dispose(); 155 mesh.material = newMat; 156 }, 157 158 updateTexture: function (material, texture){//更换纹理 159 if(!material || !texture){ 160 console.log("_Three: 纹理更换失败"); 161 return; 162 } 163 let map = new THREE.Texture(texture.image); 164 map.wrapS = THREE.RepeatWrapping; 165 map.wrapT = THREE.RepeatWrapping; 166 map.repeat.set(1, 1); 167 map.minFilter = THREE.NearestFilter; 168 map.magFilter = THREE.NearestFilter; 169 if(material.map !== null){ 170 map.wrapS = material.map.wrapS; 171 map.wrapT = material.map.wrapT; 172 map.repeat.copy(material.map.repeat); 173 map.anisotropy = material.map.anisotropy; 174 material.map.dispose(); 175 } 176 material.map = map; 177 material.map.needsUpdate = true; 178 material.needsUpdate = true; 179 }, 180 181 render: function (scene, camera, renderer){//渲染 182 renderer.render(scene, camera); 183 }, 184 185 getBSP: function (meshA, meshB, type){//剪裁 添加 重合 186 if(!meshA || !meshB || !type || meshA === meshB){console.log("getBSP:参数错误, 请选择1至2个物体进行运算"); return;} 187 if(!meshA.geometry || !meshB.geometry) return; 188 if(meshA.geometry.isGeometry !== true || meshB.geometry.isGeometry !== true) return; 189 let bsp_a = new ThreeBSP(meshA);//生成ThreeBSP对象 190 let bsp_b = new ThreeBSP(meshB);//生成ThreeBSP对象 191 let bsp = bsp_a[type](bsp_b);//进行 type 计算 192 let mesh = bsp.toMesh();//转换为mesh模型 193 mesh.geometry.computeFaceNormals();//更新模型的面 194 mesh.geometry.computeVertexNormals();//更新模型的顶点 195 mesh.material = meshA.material;//重赋值纹理 196 return mesh; 197 }, 198 199 toBuffer: function (geometry){//几何体 转为缓存几何体 200 if(geometry.isGeometry !== true) return; 201 return new THREE.BufferGeometry().fromGeometry(geometry); 202 }, 203 204 loadingGLTF: function (url, scene){//导入 205 if(!this.loadGLTF){this.__proto__.loadGLTF = new THREE.GLTFLoader();} 206 if(!scene) return; 207 this.loadGLTF.load(url, (gltf)=>{ 208 scene.add(gltf.scene); 209 }); 210 }, 211 212 exporterGLTF: function (mesh, fileType){//导出物体(限谷歌浏览器) 213 if(!this.exportGLTF){this.__proto__.exportGLTF = new THREE.GLTFExporter();} 214 if(!mesh){console.log("mesh 错误"); return;} 215 // 使用MeshBasicMaterial 或 MeshStandardMaterial 材质 效果会更好 216 //使用 BufferGeometry 缓存几何体 文件体积会更小 217 //if(mesh.geometry.isGeometry === true){mesh.geometry = new THREE.BufferGeometry().fromGeometry(mesh.geometry);} 218 var opt = { 219 binary: fileType || this.exportGLTFFileType || false, 220 embedImages: true, 221 onlyVisible: true, 222 truncateDrawRange: true, 223 trs: false 224 }; 225 var download = ( blob, filename )=>{ 226 let link = _view.add(document.body, "a"); 227 link.style.display = ‘none‘; 228 link.href = URL.createObjectURL( blob ); console.log(link.href); 229 link.download = filename; 230 link.click(); 231 _view.remove(link); 232 } 233 234 this.exportGLTF.parse(mesh, function ( result ){ 235 if(result instanceof ArrayBuffer){ 236 download(new Blob([result], {type: ‘application/octet-stream‘}), ‘scene.glb‘); 237 }else{ 238 download(new Blob([JSON.stringify( result, null, 2 )], {type: ‘text/plain‘}), ‘scene.gltf‘); 239 } 240 }, opt); 241 }, 242 243 runRaycaster: function (o, w, h){//光线投射 244 o.result.length = 0; 245 o.vector2.set((o.x / w || _width) * 2 - 1, -(o.y / h || _height) * 2 + 1); 246 o.raycaster.setFromCamera(o.vector2, o.camera); 247 o.raycaster.intersectObjects(o.children, o.recursive, o.result); 248 }, 249 250 createClipPlane: function (handCreate){//添加 面 和 点 剪裁面 251 let pa = new THREE.Plane(new THREE.Vector3( 1, 0, 0 ), 1); 252 let pb = new THREE.Plane(new THREE.Vector3( 0, -1, 0 ), 1); 253 let pc = new THREE.Plane(new THREE.Vector3( 0, 0, -1), 1); 254 let pd = new THREE.Plane(new THREE.Vector3( 0, 1, 1), 1); 255 256 let hg = new THREE.Group(); 257 hg.add(new THREE.PlaneHelper(pa, 2, 0xff0000), new THREE.PlaneHelper(pb, 2, 0x00ff00), new THREE.PlaneHelper(pc, 2, 0x0000ff), new THREE.PlaneHelper(pd, 2, 0x0000ff)); 258 return { 259 planes: [pa, pb, pc, pd], 260 helpers: hg 261 }; 262 var geometry = new THREE.SphereGeometry(1, 48, 24); 263 var material = new THREE.MeshLambertMaterial({ 264 color: new THREE.Color("#666666"), 265 side: THREE.DoubleSide, 266 clippingPlanes: clipPlane, 267 clipIntersection: true 268 }); 269 console.log(helper0); console.log(material); 270 handCreate.add(new THREE.Mesh(geometry, material), helperGroup); 271 272 handCreate.renderer.localClippingEnabled = true; 273 274 //clipPlane.constant = 0.1; 275 new Gui({constant:0}, [-1, 1, 0.01]) 276 .change((val)=>{ 277 for(let k = 0; k < clipPlane.length; k++){ 278 clipPlane[k].constant = val; 279 } 280 handCreate.update(); 281 }) 282 .add(material, "clipIntersection") 283 .add(helper0, "size", [0, 10, 0.1]) 284 .change(()=>{handCreate.update();}) 285 } 286 287 }); 288 289 290 291 //几何体 292 var _Geometry = function (){ 293 _Three.call(this); 294 //this.width = 100; _width 295 this.height = 100; 296 this.y = this.campos.y; 297 this.dis = 1.5; 298 this.targetChange = null; 299 this.objects = []; 300 } 301 302 _Geometry.prototype = Object.assign(Object.create(_Three.prototype), { 303 304 constructor: _Geometry, 305 306 config: [ 307 new THREE.BoxGeometry(1, 1, 1, 1, 1, 1), 308 new THREE.SphereGeometry(0.5, 8 , 6, 0, Math.PI * 2, 0, Math.PI), 309 new THREE.PlaneGeometry(1, 1, 1, 1) 310 ], 311 312 campos: {x: _width/110, y: 0, z: 1.5}, 313 314 defaultMaterial: new THREE.MeshBasicMaterial({wireframe: true}), 315 316 init: function (){//初始化 317 if(this.scene === undefined) this.addScene(); 318 if(this.show === undefined) this.setShow(); 319 if(this.target === undefined) this.setTarget(); 320 this.renderView(this.config); 321 return this; 322 }, 323 324 addScene: function (){//添加 场景 325 let elem = _view.add(document.body, "div", null, "ShowObject box-scroll-block"); 326 _view.addEvent(elem, ‘click‘, (e)=>{this.addTarget(e);}); 327 328 let scene = this.createScene(0x000000, false); 329 var camera = new THREE.OrthographicCamera(_width/-100, _width/100, this.height/100, this.height/-100, 0.1, 10); 330 camera.lookAt(0, 0, 0); 331 let renderer = this.createRenderer(elem, _width, this.height, true, true, true, true); 332 let linghts = this.addLinghts(scene); 333 let drag = new THREE.DragControls(scene.children, camera, renderer.domElement); 334 drag.enabled = false; 335 this.__proto__.scene = {e: elem, s:scene, c: camera, r: renderer, l: linghts, drag: drag, target: null} 336 drag.addEventListener(‘hoveron‘, (e)=>{this.scene.target = e.object;}); 337 drag.addEventListener(‘hoveroff‘, ()=>{this.scene.target = null;}); 338 this.goToPosition(); 339 }, 340 341 addLinghts: function (scene){//重写添加灯光 342 let a = new THREE.AmbientLight(0x696969); 343 let b = new THREE.DirectionalLight(0xFFFFFF, 1); 344 b.position.set(0, 5, 5); 345 scene.add(a, b); 346 return [a, b]; 347 }, 348 349 goToPosition: function (){//跳转到 geometry 坐标 350 this.scene.c.position.set(this.campos.x, this.campos.y, this.campos.z); 351 this.update(); 352 }, 353 354 setPos: function (k, mesh){//renderView-> 设置物体之间的距离 355 mesh.position.set(k * this.dis, this.y, 0); 356 }, 357 358 addMesh: function (object, key){//renderView-> 添加物体 359 let mesh = new THREE.Mesh(object, this.defaultMaterial); 360 mesh.HandCreate = {type: "_Geometry", index: key, MatIndex: 0} 361 this.scene.s.add(mesh); 362 return mesh; 363 }, 364 365 renderView: function (arr){//渲染场景视图 366 for(let k = 0; k < arr.length; k++){ 367 let mesh = this.addMesh(arr[k], k); 368 this.setPos(k, mesh); 369 this.objects.push(mesh); 370 } 371 this.update(); 372 }, 373 374 setShow: function (){//是否显示场景的html元素 375 let _show = false; 376 Object.defineProperties(this.__proto__, { 377 show: { 378 get:()=>{return _show;}, 379 set:(v)=>{ 380 if(v === true){ 381 this.scene.e.style.display = "block"; 382 }else{ 383 this.scene.e.style.display = "none"; 384 } 385 _show = v; 386 } 387 } 388 }); 389 }, 390 391 addTarget: function (e){//addScene event->场景html元素的事件回调函数 392 if(!this.scene.target) return; 393 this.target = this.scene.target; 394 }, 395 396 setTarget: function (){//配置 target 397 let _target = null; 398 Object.defineProperties(this.__proto__, { 399 target: { 400 get:()=>{return _target;}, 401 set:(v)=>{ 402 if(typeof(this.targetChange) === "function") this.targetChange(v); 403 _target = v; 404 } 405 } 406 }); 407 }, 408 409 exportObject: function (){//导出 几何体 410 411 }, 412 413 importObject: function (object){//导入 几何体 414 if(object.isGeometry !== true) return; 415 let nowKey = this.config.length; 416 let mesh = this.addMesh(object, nowKey); 417 this.setPos(nowKey, mesh); 418 this.config.push(object); 419 this.objects.push(mesh); 420 this.update(); 421 }, 422 423 update: function (){//更新场景 424 this.render(this.scene.s, this.scene.c, this.scene.r); 425 }, 426 427 debugCamera: function (){//gui调试 正交相机 428 let c = this.scene.c; 429 let pos = {x: _width/110, y: 0, lookAt: true, position: false}; 430 let upd = ()=>{ 431 if(pos.lookAt) c.lookAt(pos.x, pos.y, 0); 432 if(pos.position) c.position.set(pos.x, pos.y, 5); 433 this.update(); 434 } 435 436 new Gui(pos, [0, 10, 0.1], "调试正交相机") 437 .change({ 438 x: (v)=>{pos.x = v; upd();}, 439 y: (v)=>{pos.y = v; upd();} 440 }) 441 442 } 443 444 }); 445 446 447 448 //材质 449 var _Material = function (){ 450 _Geometry.call(this); 451 } 452 453 _Material.prototype = Object.assign(Object.create(_Geometry.prototype), { 454 455 constructor: _Material, 456 457 config:[ 458 new THREE.MeshBasicMaterial(), 459 new THREE.MeshLambertMaterial(), 460 new THREE.MeshStandardMaterial() 461 ], 462 463 defaultGeometry: new THREE.BoxGeometry(1, 1, 1, 1, 1, 1), 464 465 campos: {x: _width/110, y: 1.5, z: 1.5}, 466 467 addMesh: function (object, key){ 468 let mesh = new THREE.Mesh(this.defaultGeometry, object); 469 mesh.HandCreate = {type: "_Material", index: key}; 470 this.scene.s.add(mesh); 471 return mesh; 472 }, 473 474 goToPosition: function (mesh){ 475 if(!mesh) return; this.mesh = mesh; 476 let geometry = _Geometry.prototype.config[mesh.HandCreate.GeoCI].clone(); 477 for(let k = 0; k < this.objects.length; k++){ 478 this.updateGeometry(this.objects[k], geometry); 479 } 480 this.scene.c.position.set(this.campos.x, this.campos.y, this.campos.z); 481 this.update(); 482 } 483 484 }); 485 486 487 488 //纹理 map 489 var _Texture = function (){ 490 _Material.call(this); 491 this.textureLen = 12; 492 this.setTexture(); 493 } 494 495 _Texture.prototype = Object.assign(Object.create(_Material.prototype), { 496 497 constructor: _Texture, 498 499 config: [], 500 501 campos: {x: _width/110, y: 3, z: 1.5}, 502 503 defaultMaterial: new THREE.MeshBasicMaterial(), 504 505 load: new THREE.TextureLoader(), 506 507 loaded: 0, 508 509 setTexture: function (){ 510 let k, texture; 511 for(k = 0; k < this.textureLen; k++){ 512 texture = this.load.load("img/texture/"+k+".jpg", ()=>{this.__proto__.loaded++;}); 513 texture.wrapS = THREE.RepeatWrapping; 514 texture.wrapT = THREE.RepeatWrapping; 515 texture.repeat.set(1, 1); 516 this.__proto__.config.push(texture); 517 } 518 if(this.loaded !== this.textureLen){this.awaitLoad();} 519 }, 520 521 awaitLoad: function (){ 522 let interval = setInterval(()=>{ 523 if(this.loaded === this.textureLen){ 524 this.update(); 525 clearInterval(interval); 526 console.log("load texture: "+this.loaded+"/"+this.textureLen); 527 }else{ 528 console.log("load texture: "+this.loaded+"/"+this.textureLen); 529 } 530 }, 300); 531 }, 532 533 addMesh: function (object, key){ 534 let mesh = new THREE.Mesh(this.defaultGeometry, new THREE.MeshBasicMaterial({map: object})); 535 //let mesh = new THREE.Mesh(this.defaultGeometry, this.defaultMaterial); 536 //this.updateTexture(mesh, object); 537 mesh.HandCreate = {type: "_Texture", index: key}; 538 this.scene.s.add(mesh); 539 return mesh; 540 }, 541 542 goToPosition: function (mesh){ 543 if(!mesh) return; this.mesh = mesh; 544 let geometry = _Geometry.prototype.config[mesh.HandCreate.GeoCI].clone(); 545 let material = _Material.prototype.config[mesh.HandCreate.MatCI].clone(); 546 for(let k = 0; k < this.objects.length; k++){ 547 this.updateGeometry(this.objects[k], geometry); 548 this.updateMaterial(this.objects[k], material); 549 } 550 this.scene.c.position.set(this.campos.x, this.campos.y, this.campos.z); 551 this.update(); 552 } 553 554 }); 555 556 //物体 557 var _Mesh = function (){} 558 559 //gui 560 var SetView = function (HC, newMesh, oldMesh){ 561 this.HC = HC; 562 this.newMesh = newMesh; 563 this.oldMesh = oldMesh; 564 return this.init(); 565 } 566 567 Object.assign(SetView.prototype, { 568 569 constructor: SetView, 570 571 init: function (){ 572 if(this.globalGui === undefined) this.createGlobal(); 573 this.gui = new Gui().display("none"); 574 575 this.createMesh(); 576 this.createGeometry(); 577 this.createMaterial(); 578 return this; 579 }, 580 581 createMesh: function (){ 582 let o = { 583 exportGltf: ()=>{this.HC.Three.exporterGLTF(this.newMesh);}, 584 addMesh: ()=>{this.HC.geometry.goToPosition();}, 585 removeMesh: ()=>{this.HC.remove(this.newMesh);}, 586 getBSP: "intersect", 587 runGetBSP: ()=>{ 588 let meshB = this.HC.targets[0].mesh; 589 if(this.newMesh === meshB) meshB = this.HC.targets[1].mesh; 590 let result = this.HC.Three.getBSP(this.newMesh, meshB, o.getBSP); 591 this.HC.geometry.importObject(result.geometry); 592 this.HC.isSelectTargets = false; 593 } 594 } 595 let m = { 596 visible: this.newMesh.visible, 597 frustumCulled: this.newMesh.frustumCulled, 598 castShadow: this.newMesh.castShadow, 599 receiveShadow: this.newMesh.receiveShadow, 600 } 601 602 this.gui.create(m, true).name({visible: "显示物体", castShadow: "投射阴影", receiveShadow: "接收阴影"}) 603 .change({ 604 visible: ()=>{ 605 if(this.newMesh.visible === undefined) return; 606 this.newMesh.visible = m.visible; 607 this.HC.update(); 608 }, 609 frustumCulled: ()=>{ 610 if(this.newMesh.frustumCulled === undefined) return; 611 this.newMesh.frustumCulled = m.frustumCulled; 612 this.HC.update(); 613 }, 614 castShadow: ()=>{ 615 if(this.newMesh.castShadow === undefined) return; 616 this.newMesh.castShadow = m.castShadow; 617 this.HC.update(); 618 }, 619 receiveShadow: ()=>{ 620 if(this.newMesh.receiveShadow === undefined) return; 621 this.newMesh.receiveShadow = m.receiveShadow; 622 this.HC.update(); 623 } 624 }) 625 626 .add(o, {getBSP:[["交集", "intersect"], ["并集", "union"], ["差集", "subtract"]]}, true) 627 .name({exportGltf: "导出物体", addMesh: "添加物体", removeMesh: "移除物体", getBSP: "布尔运算类型(选择一个物体)", runGetBSP: "布尔运算"}) 628 .change({ 629 getBSP: (e)=>{o.getBSP = e.target.value;} 630 }) 631 632 .title("物体-"+this.newMesh.type+"-"+this.newMesh.id); 633 }, 634 635 createGeometry: function (){ 636 if(this.oldMesh.geometry.parameters === undefined){console.log("SetView: 忽略了geometry"); return;} 637 let o, p = {}, geo; 638 for(let k in this.oldMesh.geometry.parameters){p[k] = this.oldMesh.geometry.parameters[k];} 639 switch(this.oldMesh.geometry.type){ 640 641 case "BoxGeometry": 642 o = { 643 width:[0.1, 10, 0.1], 644 height:[0.1, 10, 0.1], 645 depth:[0.1, 10, 0.1], 646 widthSegments:[1, 30, 1], 647 heightSegments:[1, 30, 1], 648 depthSegments:[1, 30, 1] 649 } 650 this.gui.create(p, o, true) 651 .name({width: "宽度", height: "高度", depth: "深度", widthSegments: "宽段数", heightSegments: "高段数", depthSegments: "深段数"}) 652 .change(()=>{ 653 geo = new THREE.BoxGeometry(p.width, p.height, p.depth, p.widthSegments, p.heightSegments, p.depthSegments); 654 this.HC.Three.updateGeometry(this.newMesh, geo); 655 this.HC.update(); 656 }); 657 658 break; 659 660 case "SphereGeometry": 661 o = { 662 radius:[0.1, 10, 0.1], 663 widthSegments:[1, 30, 1], 664 heightSegments:[1, 30, 1], 665 phiStart:[0, Math.PI*2, 0.1], 666 phiLength:[0, Math.PI*2, 0.1], 667 thetaStart:[0, Math.PI*2, 0.1], 668 thetaLength:[0, Math.PI*2, 0.1] 669 } 670 this.gui.create(p, o, true) 671 .name({radius: "半径", widthSegments: "宽段数", heightSegments: "高段数", phiStart: "经线起始角度", phiLength: "经线起始角度大小", thetaStart: "纬线起始角度", thetaLength: "纬线起始角度大小",}) 672 .change(()=>{ 673 geo = new THREE.SphereGeometry(p.radius, p.widthSegments, p.heightSegments, p.phiStart, p.phiLength, p.thetaStart, p.thetaLength); 674 this.HC.Three.updateGeometry(this.newMesh, geo); 675 this.HC.update(); 676 }); 677 678 break; 679 680 case "PlaneGeometry": 681 o = { 682 width: [1, 100, 0.1], 683 height: [1, 100, 0.1], 684 widthSegments: [1, 30, 1], 685 heightSegments: [1, 30, 1] 686 } 687 this.gui.create(p, o, true) 688 .name({width: "宽度", height: "高度", widthSegments: "宽段数", heightSegments: "高段数"}) 689 .change(()=>{ 690 geo = new THREE.PlaneGeometry(p.width, p.height, p.widthSegments, p.heightSegments); 691 this.HC.Three.updateGeometry(this.newMesh, geo); 692 this.HC.update(); 693 }); 694 break; 695 696 default : break; 697 } 698 this.gui.title("几何体-"+this.newMesh.geometry.type+"-"+this.newMesh.id); 699 }, 700 701 createMaterial: function (){ 702 let mat = this.newMesh.material; 703 let o = { 704 color: "#" + mat.color.getHexString(), 705 emissive: mat.emissive === undefined ? "#000000" : "#" + mat.emissive.getHexString(), 706 updateMaterial: ()=>{this.HC.material.goToPosition(this.newMesh);}, 707 updateTexture: ()=>{this.HC.texture.goToPosition(this.newMesh);} 708 } 709 let m = { 710 wireframe: mat.wireframe, 711 visible: mat.visible, 712 side: mat.side, 713 fog: mat.fog, 714 aoMapIntensity: mat.aoMapIntensity, 715 colorWrite: mat.colorWrite, 716 transparent: mat.transparent, 717 opacity: mat.opacity, 718 alphaTest: mat.alphaTest, 719 flatShading: mat.flatShading, 720 emissiveIntensity: mat.emissiveIntensity === undefined ? 0 : mat.emissiveIntensity, 721 repeatU: 1, 722 repeatV: 1, 723 anisotropy: 1 724 } 725 726 let num = { 727 aoMapIntensity: [0, 1, 0.1], 728 opacity: [0, 1, 0.1], 729 alphaTest: [0, 1, 0.1], 730 emissiveIntensity: [0, 1, 0.1], 731 repeatU: [1, 100, 1], 732 repeatV: [1, 100, 1], 733 anisotropy: [1, this.HC.renderer.capabilities.getMaxAnisotropy() || 2, 1] 734 }; 735 let sel = { 736 side: [["外面", THREE.FrontSide], ["里面", THREE.BackSide], ["双面", THREE.DoubleSide]] 737 }; 738 let name = { 739 wireframe:"网格模式", 740 visible:"显示材质", 741 side: "材质显示那一面", 742 fog: "受雾气影响", 743 aoMapIntensity: "遮挡效果", 744 colorWrite: "渲染颜色", 745 transparent: "渲染透明度", 746 opacity: "透明度", 747 alphaTest: "alpha值", 748 flatShading: "平面着色", 749 emissiveIntensity: "放射光强度", 750 repeatU: "重复量,纹理U面", 751 repeatV: "重复量,纹理V面", 752 anisotropy: "纹理清晰度" 753 }; 754 let c = { 755 wireframe: ()=>{if(this.newMesh.material.wireframe === undefined){this.gui.showStop("wireframe", m); return;}else{this.gui.showStop("wireframe", m, false);} this.newMesh.material.wireframe = m.wireframe; this.HC.update();}, 756 visible: ()=>{if(this.newMesh.material.visible === undefined){this.gui.showStop("visible", m); return;}else{this.gui.showStop("visible", m, false);} this.newMesh.material.visible = m.visible; this.HC.update();}, 757 side: (e)=>{if(this.newMesh.material.side === undefined){this.gui.showStop("side", m); return;}else{this.gui.showStop("side", m, false);} this.newMesh.material.side = eval(e.target.value); this.HC.update();}, 758 fog: ()=>{if(this.newMesh.material.fog === undefined){this.gui.showStop("fog", m); return;}else{this.gui.showStop("fog", m, false);} this.newMesh.material.fog = m.fog; this.HC.update();}, 759 aoMapIntensity: ()=>{if(this.newMesh.material.aoMapIntensity === undefined){this.gui.showStop("aoMapIntensity", m); return;}else{this.gui.showStop("aoMapIntensity", m, false);} this.newMesh.material.aoMapIntensity = m.aoMapIntensity; this.HC.update();}, 760 colorWrite: ()=>{if(this.newMesh.material.colorWrite === undefined){this.gui.showStop("colorWrite", m); return;}else{this.gui.showStop("colorWrite", m, false);} this.newMesh.material.colorWrite = m.colorWrite; this.HC.update();}, 761 transparent: ()=>{if(this.newMesh.material.transparent === undefined){this.gui.showStop("transparent", m); return;}else{this.gui.showStop("transparent", m, false);} this.newMesh.material.transparent = m.transparent; this.HC.update();}, 762 opacity: ()=>{if(this.newMesh.material.opacity === undefined){this.gui.showStop("opacity", m); return;}else{this.gui.showStop("opacity", m, false);} this.newMesh.material.opacity = m.opacity; this.HC.update();}, 763 alphaTest: ()=>{if(this.newMesh.material.alphaTest === undefined){this.gui.showStop("alphaTest", m); return;}else{this.gui.showStop("alphaTest", m, false);} this.newMesh.material.alphaTest = m.alphaTest; this.HC.update();}, 764 flatShading: ()=>{if(this.newMesh.material.flatShading === undefined){this.gui.showStop("flatShading", m); return;}else{this.gui.showStop("flatShading", m, false);} this.newMesh.material.flatShading = m.flatShading; this.HC.update();}, 765 emissiveIntensity: ()=>{if(this.newMesh.material.emissiveIntensity === undefined){this.gui.showStop("emissiveIntensity", m); return;}else{this.gui.showStop("emissiveIntensity", m, false);} this.newMesh.material.emissiveIntensity = m.emissiveIntensity; this.HC.update();}, 766 repeatU: ()=>{if(this.newMesh.material.map === null){this.gui.showStop("repeatU", m); return;}else{this.gui.showStop("repeatU", m, false);} this.newMesh.material.map.repeat.set(m.repeatU, m.repeatV);this.HC.update();}, 767 repeatV: ()=>{if(this.newMesh.material.map === null){this.gui.showStop("repeatV", m); return;}else{this.gui.showStop("repeatV", m, false);} this.newMesh.material.map.repeat.set(m.repeatU, m.repeatV); this.HC.update();}, 768 anisotropy: ()=>{if(this.newMesh.material.map === null || (m.anisotropy !== 1 && (m.anisotropy / 2 % 1) !== 0)){this.gui.showStop("anisotropy", m); return;}else{this.gui.showStop("anisotropy", m, false);}this.newMesh.material.map.anisotropy = m.anisotropy;this.HC.update();}, 769 770 }; 771 772 this.gui.create(m, num, true, sel).name(name).change(c) 773 774 .add(o, true) 775 .name({color: "材质颜色", emissive: "放射光颜色", updateMaterial: "替换材质", updateTexture: "替换纹理"}) 776 .change({ 777 color: (e)=>{if(this.newMesh.material.color === undefined){this.gui.showStop("color", o); return;}else{this.gui.showStop("color", o, false);} this.newMesh.material.color.set(e.target.value); this.HC.update();}, 778 emissive: (e)=>{if(this.newMesh.material.emissive === undefined){this.gui.showStop("emissive", o); return;}else{this.gui.showStop("emissive", o, false);} this.newMesh.material.emissive.set(e.target.value); this.HC.update();} 779 }) 780 781 .title("材质-"+this.newMesh.material.type+"-"+this.newMesh.id); 782 }, 783 784 createGlobal: function (){ 785 let o = { 786 setMode: "translate", 787 exportAll: ()=>{console.log("导出全部物体")}, 788 updateMaterialAll: ()=>{console.log("替换全部材质")}, 789 isgltf: true 790 } 791 792 let gui = new Gui(this.HC.geometry, "show", "显示创建选项") 793 794 .add(o, true, {isgltf: [[".gltf", false], [".glb", true]], setMode: [["平移", "translate"], ["旋转", "rotate"], ["缩放", "scale"]]}) 795 .name({exportAll: "导出全部物体", updateMaterialAll: "替换全部材质", setMode: "控制模式", isgltf:"导出类型"}) 796 .change({ 797 isgltf: (e)=>{this.HC.Three.exportGLTFFileType = eval(e.target.value);}, 798 setMode: (e)=>{this.HC.control.transform.setMode(e.target.value);} 799 }) 800 801 .add(this.HC, true, ["lockTarget", "isSelectTargets"]) 802 .name({lockTarget: "锁定物体", isSelectTargets: "选择物体"}) 803 .change({ 804 isSelectTargets: ()=>{ 805 if(this.HC.isSelectTargets === false){ 806 this.HC.removeTargets(); 807 } 808 }, 809 lockTarget: ()=>{ 810 if(this.HC.lockTarget === false){ 811 this.HC.removeTargets(); 812 } 813 } 814 }) 815 816 .title("global"); 817 this.__proto__.globalGui = gui; 818 } 819 820 }); 821 822 823 824 //main 825 var HandCreate = function (){ 826 this.Three = new _Three(); 827 this.group = new THREE.Group(); 828 this.target = {}; 829 this.isSelectTargets = false; 830 this.targets = []; 831 this.backMesh = null; 832 this.lockTarget = false; 833 this.scene = this.Three.createScene([], false); 834 this.renderer = this.Three.createRenderer(document.body, _width, _height, true, true, true, true); 835 836 } 837 838 Object.assign(HandCreate.prototype, { 839 840 constructor: HandCreate, 841 842 init: function (){ 843 this.intersects = { 844 raycaster: new THREE.Raycaster(), 845 group: new THREE.Group(), 846 vector2: new THREE.Vector2(), 847 result: [], 848 camera: this.camera, 849 recursive: true, 850 x: 0, 851 y: 0, 852 }; 853 this.renderer.domElement.style = "position:absolute; top:0; left:0";//如果不定位, gui的移动块会不正常显示 854 //this.renderer.localClippingEnabled = true;//是否渲染 平面剪裁 855 this.camera = this.Three.createCamera(45, _width, _height, _minDistance, _maxDistance * _minDistance * 0.2); 856 this.camera.position.set(0, _maxDistance * 0.001, _maxDistance * 0.001); 857 this.linghts = this.Three.addLinghts(this.scene, this); 858 this.control = this.Three.addControls(this.scene, this.camera, this.renderer, this.intersects.group.children); 859 this.control.drag.addEventListener(‘hoveron‘, (e)=>{this.setTarget(e.object);}); 860 861 this.geometry = new _Geometry().init(); 862 this.geometry.targetChange = (v)=>{this.createObject(v);}; 863 this.geometry.show = true; 864 865 this.material = new _Material().init(); 866 867 this.texture = new _Texture().init(); 868 869 this.gridHelper = new THREE.GridHelper(_maxDistance, _maxDistance/1); 870 this.scene.add(this.group, this.intersects.group, this.camera, this.gridHelper); 871 872 //this.clip = this.Three.createClip(); 873 //this.globalView(); 874 //this.Three.loadingGLTF("img/gltfs/scene.gltf", this.intersects.group); 875 //this.Three.loadingGLTF("img/gltfs/scene.glb", this.intersects.group); 876 return this; 877 }, 878 879 add: function (mesh, group){ 880 if(!mesh) return; 881 let gro = group || this.intersects.group; 882 if(Array.isArray(mesh) === true){ 883 let k, len = mesh.length; 884 for(k; k < len; k++){ 885 if(!mesh[k]) continue; 886 gro.add(mesh[k]); 887 } 888 }else{ 889 gro.add(mesh); 890 } 891 this.update(); 892 }, 893 894 remove: function (mesh, group){ 895 if(!mesh){console.log("remove: mesh不存在"); return;} 896 let arr = group || this.intersects.group; 897 if(!arr.remove){console.log("remove: group错误"); return;} 898 this.removeTarget(); 899 if(mesh.gui) mesh.gui.remove(); 900 mesh.HandCreate.SV.gui.remove(); 901 if(mesh.material.map){mesh.material.map.dispose();} 902 mesh.material.dispose(); 903 mesh.geometry.dispose(); 904 arr.remove(mesh); 905 this.update(); 906 }, 907 908 update: function (){ 909 this.Three.render(this.scene, this.camera, this.renderer); 910 }, 911 912 setTarget: function (mesh){ 913 if(this.isSelectTargets === true){this.setTargets(mesh);} 914 if(this.target.mesh === mesh || this.lockTarget === true){return;} 915 if(mesh.HandCreate === undefined || mesh.HandCreate.SV === undefined){this.lockTarget = true; console.log("setTarget: mesh 错误,取消选择"); return;} 916 917 this.removeTarget(); 918 919 this.target.mesh = mesh; 920 this.control.transform.attach(mesh); 921 mesh.HandCreate.SV.gui.display("block"); 922 this.geometry.goToPosition(); 923 this.update(); 924 }, 925 926 removeTarget: function (){ 927 if(!this.target.mesh) return; 928 this.control.transform.detach(this.target.mesh); 929 this.target.mesh.HandCreate.SV.gui.display("none"); 930 delete(this.target.mesh); 931 this.update(); 932 }, 933 934 setTargets: function (mesh){ 935 //if(this.targets.indexOf(mesh) !== -1){return;} 936 for(let k = 0; k < this.targets.length; k ++){if(this.targets[k].mesh === mesh){return;}} 937 this.isSelectTargets = true; 938 this.lockTarget = true; 939 this.targets.push({mesh: mesh, color: mesh.material.color.getHex()}); 940 mesh.material.color.set("red"); 941 this.update(); 942 }, 943 944 removeTargets: function (){ 945 this.isSelectTargets = false; 946 this.lockTarget = false; 947 for(let k = 0; k < this.targets.length; k++){ 948 this.targets[k].mesh.material.color.set(this.targets[k].color); 949 } 950 this.targets.length = 0; 951 this.update(); 952 }, 953 954 createObject: function (v){ 955 956 let geo = ()=>{ 957 let mesh = new THREE.Mesh(v.geometry.clone(), v.material.clone()); 958 mesh.material.transparent = true; 959 mesh.castShadow = true; 960 mesh.receiveShadow = true; 961 mesh.customDepthMaterial = new THREE.MeshDepthMaterial({depthPacking: THREE.RGBADepthPacking}); 962 963 mesh.HandCreate = { 964 SV: new SetView(this, mesh, v), 965 GeoCI: v.HandCreate.index, 966 MatCI: v.HandCreate.MatIndex, 967 TexCI: null 968 }; 969 970 this.backMesh = mesh; 971 this.add(mesh); 972 this.setTarget(mesh); 973 } 974 975 let mat = ()=>{ 976 this.Three.updateMaterial(this.material.mesh, v.material); 977 this.material.mesh.HandCreate.MatCI = v.HandCreate.index; 978 this.update(); 979 } 980 981 let tex = ()=>{ 982 this.Three.updateTexture(this.texture.mesh.material, v.material.map); 983 this.texture.mesh.HandCreate.TexCI = v.HandCreate.index; 984 this.update(); 985 } 986 987 switch(v.HandCreate.type){ 988 case "_Geometry": geo(); break; 989 case "_Material": mat(); break; 990 case "_Texture": tex(); break; 991 default: break; 992 } 993 994 } 995 996 }); 997 998 //代理 999 /* this.ForCreate = new Proxy(new HandCreate().init(), { 1000 get(o, k){ 1001 return o[k]; 1002 }, 1003 set(o, k, v){ 1004 o[k] = v; 1005 } 1006 }); */ 1007 1008 this.ForCreate = new HandCreate().init(); 1009 1010 }).call(this)
以上是关于javascript 3d网页 简单的 可视化编辑 图形界面 搭建几何体 带导出物体 示例 ( three.js r114 初探 六)的主要内容,如果未能解决你的问题,请参考以下文章
javascript 3d网页 简单几行代码创建一个动态水潭, 湖面 示例 ( three.js r114 初探 四)
javascript 3d网页 原创简单的Gui控制视图类 和 ThreeBSP网格组合 ( three.js r114 初探 三)