您好,登錄后才能下訂單哦!
這篇文章主要介紹怎么使用Chrome控制臺進行3D模型編輯的實現,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
前言:3D模型編輯的核心是對頂點位置和紋理顏色的編輯,這個研究的目的在于尋找一種通過編程方式直接對模型進行編輯的方法,這種編輯方法和時下流行的通過鼠標點選、拖拽進行編輯的方法之間的關系,和前端編程中“程序員編寫靜態網頁”與“美工進行網頁切圖”之間的關系很相似。
一、工具用法:
1、訪問 https://ljzc002.github.io/SnowMiku/HTML/MakeRibbon.html打開條帶網格生成器頁面
在場景世界坐標系的(0,-10,0),(0,0,0),(0,10,0)處各有一個綠色小球作為參考點,使用上下左右和鼠標拖動可以進行場景漫游。
2、按F12鍵打開Chrome控制臺,在控制臺中輸入:MakeRibbon(MakeRing(5,12),-10,2,11,"mesh_ribbon")回車:
在場景中繪制了一個半徑為5,曲面細分度為12,左端位于-10,每兩個圓環間距2,共由11個圓環組成的圓柱面。
拉近查看:
3、輸入ShowNormals(mesh_origin)將用紅色線段顯示每個頂點的法線方向
輸入DeleteMeshes([lines_normal])可以刪除所有的法線,輸入DeleteMeshes([mesh_origin])則刪除圓柱面網格。
4、鼠標移入網格上的三角形,會顯示三角形的頂點信息:
其中“1:2-5”表示這是三角形的第一個頂點,這個頂點位于索引是2的圓環上(第三個圓環),這個頂點在圓環中的索引是5(也就是第六個頂點)。
5、輸入PickPoints([[2,5],[3,5],[2,6]],mesh_origin)可以選定這些頂點
被選中頂點所影響的所有邊框線標示為黃色,這個“選中”只是改變外觀而已。
6、輸入TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.Translation(0,0,-10))將所選的頂點向z軸負方向移動10,被移動的頂點和前面選中的頂點其實沒有關系,其中arr_ij也可以直接用索引數組[[2,5],[3,5],[2,6]]代替。
另一類變形可以通過輸入:TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.RotationX(Math.PI/2))實現,這可以把被選中的頂點繞X中旋轉90度。
輸入DeleteMeshes([lines_inpicked])取消被選中的效果,輸入ChangeMaterial(mesh_origin,mat_blue)將邊框換成藍色紋理:
可以看到變形后的效果,接下來還可以繼續選擇頂點并變形
7、輸入ExportMesh("1",mat_blue),以txt格式導出babylon模型文件,文件名為“1.txt”
8、將導出的txt改名為9.babylon后放入網站目錄中,訪問https://ljzc002.github.io/SnowMiku/HTML/LoadBabylon.html模型預覽頁面,在控制臺輸入ImportMesh("","../ASSETS/SCENE/","9.babylon")即可加載剛才導出的模型。
二、編程思路:
1、首先要建立一個可以進行各種測試的基礎場景,使用的代碼如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>建立一個條帶網格生成器,能夠輸入參數生成起始條帶,然后通過命令行選取并修改pathArray,最后導出生成的條帶</title> <link href="../CSS/newland.css" rel="stylesheet"> <link href="../CSS/stat.css" rel="stylesheet"> <script src="../JS/MYLIB/Events.js"></script> <script src="../JS/MYLIB/FileText.js"></script> <script src="../JS/MYLIB/View.js"></script> <script src="../JS/LIB/babylon.32.all.maxs.js"></script><!--V3.2的穩定版本--> <script src="../JS/MYLIB/newland.js"></script> <script src="../JS/LIB/stat.js"></script> </head> <body> <div id="div_allbase"> <canvas id="renderCanvas"></canvas> <div id="fps" style="z-index: 301;"></div> </div> </body> <script> var VERSION=1.0,AUTHOR="lz_newland@163.com"; var machine,canvas,engine,scene,gl,MyGame={}; canvas = document.getElementById("renderCanvas"); engine = new BABYLON.Engine(canvas, true); gl=engine._gl;//可以結合使用原生OpenGL和Babylon.js; scene = new BABYLON.Scene(engine); var divFps = document.getElementById("fps"); window.onload=beforewebGL; function beforewebGL() { if(engine._webGLVersion==2.0)//輸出ES版本 { console.log("ES3.0"); } else{ console.log("ES2.0"); } //MyGame=new Game(0,"first_pick","","http://127.0.0.1:8082/"); /*0-startWebGL * */ webGLStart(); } //從下面開始分成簡單測試和對象框架兩種架構 //簡單測試 //全局對象 var light0//全局光源 ,camera0//主相機 ; //四種常用材質 var mat_frame = new BABYLON.StandardMaterial("mat_frame", scene); mat_frame.wireframe = true; var mat_red = new BABYLON.StandardMaterial("mat_red", scene); mat_red.diffuseColor = new BABYLON.Color3(1, 0, 0); mat_red.backFaceCulling=false; var mat_green = new BABYLON.StandardMaterial("mat_green", scene); mat_green.diffuseColor = new BABYLON.Color3(0, 1, 0); mat_green.backFaceCulling=false; var mat_blue = new BABYLON.StandardMaterial("mat_blue", scene); mat_blue.diffuseColor = new BABYLON.Color3(0, 0, 1); mat_blue.backFaceCulling=false; var mesh_origin; var advancedTexture=BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui1");//全屏GUI function webGLStart() { window.addEventListener("resize", function () {//自動調整視口尺寸 engine.resize(); }); camera0 =new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 0, -80), scene); camera0.attachControl(canvas, true); camera0.speed=0.5;//相機移動速度是默認速度的一半 camera0.minZ=0.01;//相機位置距前視錐截面的距離,也就是說到相機距離小于0.01的圖元都不會顯示,這個值不能過小,否則Babylon.js內置的鼠標選取將失效 camera0.layerMask=2;//相機的遮罩層次,這個相機將只能顯示遮罩層次同為2的網格,如果不設置這個屬性,似乎可以顯示所有遮罩層次的網格 scene.activeCameras.push(camera0);//將相機加入活躍相機列表,默認情況下Babylon.js只使用一個活躍相機,但是也可以強行使用多個 light0 = new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 1, 0), scene);//半球光源 //三個參照物,MeshBuilder是新版Babylon.js中使用的網格構建對象,之前翻譯入門教程時還沒有這個對象,它的特點是把一大堆參數統一整理到一個option參數中 var mesh_base=new BABYLON.MeshBuilder.CreateSphere("mesh_base",{diameter:1},scene); mesh_base.material=mat_green; mesh_base.position.x=0; mesh_base.layerMask=2; var mesh_base1=new BABYLON.MeshBuilder.CreateSphere("mesh_base1",{diameter:1},scene); mesh_base1.position.y=10; mesh_base1.position.x=0; mesh_base1.material=mat_green; mesh_base1.layerMask=2; var mesh_base2=new BABYLON.MeshBuilder.CreateSphere("mesh_base2",{diameter:1},scene); mesh_base2.position.y=-10; mesh_base2.position.x=0; mesh_base2.material=mat_green; mesh_base2.layerMask=2; 。 。 。 MyBeforeRender(); } function MyBeforeRender() { scene.registerBeforeRender(function() { if(scene.isReady()) { 。 。 。 。 。 。 } }); engine.runRenderLoop(function () { engine.hideLoadingUI(); if (divFps) { // Fps divFps.innerHTML = engine.getFps().toFixed() + " fps"; } scene.render(); }); } </script> </html>
這個3D場景包括了簡單測試所需要的一些基本元素,這里使用的是包含全部組件的未壓縮版Babylon.js庫,在實際使用中考慮到節省帶寬,可以使用Babylon.js官網提供的工具定制精簡版或壓縮版的Babylon.js庫
2、建立一個基礎網格
計劃通過對一個基礎網格進行頂點變換來產生各種各樣的簡單模型,在Babylon.js中“條帶”是一種非常適合頂點變換的網格類型,Babylon.js官方教程中有關于條帶構造和變形的文檔,可以在這里下載中文翻譯http://down.51cto.com/data/2449757。
用來建立基礎網格的代碼如下:
//下面這些函數都通過控制臺調用 //在ZoY平面里建立一個圓環路徑 //radius:半徑,sumpoint:使用幾個點 function MakeRing(radius,sumpoint) { var arr_point=[]; var radp=Math.PI*2/sumpoint; for(var i=0.0;i<sumpoint;i++) { var x=0; var rad=radp*i; //var y=sswr(radius*Math.sin(rad),null,5);//在這里需要降低一些精確度?否則Babylon.js在計算頂點數據時可能和這里不一致? //var z=sswr(radius*Math.cos(rad),null,5); var y=radius*Math.sin(rad); var z=radius*Math.cos(rad); arr_point.push(new BABYLON.Vector3(x,y,z)); } arr_point.push(arr_point[0].clone());//首尾相連,不能這樣相連,否則變形時會多出一個頂點!!,看來這個多出的頂點無法去掉,只能在選取時額外處理它 return arr_point; } var arr_path=[];//核心數據 //arr_point:單個路徑的點數組,xstartl:第一個扁平路徑放在最左側,spacing:路徑的間距,sumpath:一共使用幾條路徑, function MakeRibbon(arr_point,xstartl,spacing,sumpath,name) {//將一條圓環路徑擴展成相互平行的多個圓環路徑,然后使用這些路徑生成條帶 arr_path=[]; for(var i=0;i<sumpath;i++)//對于每一條路徑 { var x=xstartl+spacing*i; //var arr=arr_point.concat(null);//為什么拷貝失靈了? //var [ ...arr ] = arr_point;//ES6的新擴展運算符?-》也不好使,因為數組里的元素是指針?!! var len=arr_point.length; var arr=[]; for(var j=0;j<len;j++) { var obj=arr_point[j].clone(); obj.x=x; // arr.push(obj); } arr_path.push(arr); arr=null; } mesh_origin.dispose(); mesh_origin=BABYLON.MeshBuilder.CreateRibbon(name,{pathArray:arr_path,updatable:true,closePath:false,closeArray:false}); //mesh_origin=mesh;//用一個全局變量保存最終會被導出的mesh mesh_origin.sideOrientation=BABYLON.Mesh.DOUBLESIDE;//顯示網格的前后兩面 mesh_origin.material=mat_frame; mesh_origin.layerMask=2; }
編程中遇到的幾個問題:
a、路徑中設置的坐標值在實際顯示時可能發生微小的變化,比如5可能變成4.999999999999999999,但是似乎沒什么影響。
b、雖然圓環路徑分成12段應該由12個頂點組成,但是如果只有12個頂點,那么在調用CreateRibbon方法時,如果把closePath參數設為true則Babylon.js會自動添加一個不受控制的頂點來閉合圓環路徑,如果設為false,則路徑無法閉合。為此在第18行再添加一個與起始點
重合的頂點使圓環路徑閉合。
c、原計劃直接使用數組的concat方法復制arr_point數組產生更多的圓環路徑,但concat方法似乎只能對一維數組使用(棧?),而arr_point的每個元素都是一個BABYLON.Vector3對象,所以只好用for循環一個點一個點的生成路徑
d、MakeRing是一個生成圓環路徑的方法,使用者可以根據需要編寫各種其他類型的路徑生成方法。
3、調整網格的屬性:
可以對網格的屬性進行一些調整,但是這些調整只在這個編輯器里生效,并不會被導出。
比如對網格材質的調整:
function ChangeMaterial(mesh,mat) { mesh.material=mat; }
4、顯示網格頂點法線方向
代碼如下:
var lines_normal={}; /*ShowNormals(mesh_origin) DeleteMeshes([lines_normal]); * */ //顯示所有的頂點法線 function ShowNormals(mesh) { //DeleteMeshes(arr_line_normal); if(lines_normal.dispose) { lines_normal.dispose(); } //遍歷頂點 var vb=mesh.geometry._vertexBuffers; var data_pos=vb.position._buffer._data;//頂點數據 var data_mormal=vb.normal._buffer._data;//法線數據 var len=data_pos.length; var lines=[]; for(var i=0;i<len;i+=3) {//CreateLineSystem使用一個網格包含很多分立的線段(路徑),CreateLines則是一條首尾相連的路徑 // var vec=new BABYLON.Vector3(data_pos[i],data_pos[i+1],data_pos[i+2]); var vec2=vec.clone().add(new BABYLON.Vector3(data_mormal[i],data_mormal[i+1],data_mormal[i+2]).normalize().scale(1)); lines.push([vec,vec2]); } lines_normal=new BABYLON.MeshBuilder.CreateLineSystem("lines_normal",{lines:lines,updatable:false},scene); lines_normal.color=new BABYLON.Color3(1, 0, 0); }
Babylon.js內置的CreateLineSystem方法可以方便的建立一個由很多條線段組成的網格。
5、刪除網格
代碼如下:
function DeleteMeshes(arr)//假設一個數組里的都是mesh,徹底清空它 { var len=arr.length; for(var i=0;i<len;i++) { if(arr[i].dispose) arr[i].dispose(); } arr=[]; }
值得注意的是,直接在控制臺里執行mesh.dispose()并不生效。
這里也體現出CreateLineSystem的方便之處——刪除法線時只需要dispose一個對象。
6、鼠標移入時顯示三角形信息
鼠標動作監聽代碼:
mesh_origin=new BABYLON.Mesh("mesh_origin",scene);//建立一個空的初始網格對象 mesh_surface=new BABYLON.Mesh("mesh_surface",scene); mesh_surface0=new BABYLON.Mesh("mesh_surface0",scene); if(true) {//初始化三個GUI標簽 label_index1=new BABYLON.GUI.TextBlock(); label_index1.text = "label_index1"; label_index1.color="white"; label_index1.isVisible=false;//初始化時標簽不可見 //label_index1.linkWithMesh(mesh_surface0);//TextBlock并不是頂層元素 advancedTexture.addControl(label_index1); label_index2=new BABYLON.GUI.TextBlock(); label_index2.text = "label_index2"; label_index2.color="white"; label_index2.isVisible=false; //label_index2.linkWithMesh(mesh_surface0); advancedTexture.addControl(label_index2); label_index3=new BABYLON.GUI.TextBlock(); label_index3.text = "label_index3"; label_index3.color="white"; label_index3.isVisible=false; //label_index3.linkWithMesh(mesh_surface0); advancedTexture.addControl(label_index3); } //監聽鼠標移動 canvas.addEventListener("mousemove", function(evt){ var pickInfo = scene.pick(scene.pointerX, scene.pointerY,null,null,camera0);//如果同時有多個激活的相機,則要明確的指出使用哪個相機 //cancelPropagation(evt); //cancelEvent(evt); label_index1.isVisible=false; label_index2.isVisible=false; label_index3.isVisible=false; if(mesh_surface.dispose) { mesh_surface.dispose(); } if(mesh_surface0.dispose) { mesh_surface0.dispose(); } if(pickInfo.hit&&(pickInfo.pickedMesh.name=="mesh_origin"||pickInfo.pickedMesh.name=="mesh_ribbon"))//找到鼠標所在的三角形并重繪之 { //在鼠標所指的地方繪制一個三角形,表示被選中的三角形 var faceId=pickInfo.faceId; var pickedMesh=pickInfo.pickedMesh; var indices=[pickedMesh.geometry._indices[faceId*3] ,pickedMesh.geometry._indices[faceId*3+1],pickedMesh.geometry._indices[faceId*3+2]]; var vb=pickedMesh.geometry._vertexBuffers; var position=vb.position._buffer._data; var normal=vb.normal._buffer._data; var uv=vb.uv._buffer._data; var len=arr_path[0].length; var p1={index:indices[0],position:[position[indices[0]*3],position[indices[0]*3+1],position[indices[0]*3+2]] ,normal:[normal[indices[0]*3],normal[indices[0]*3+1],normal[indices[0]*3+2]] ,uv:[uv[indices[0]*2],uv[indices[0]*2+1]],ppi:("1:"+(Math.round(indices[0]/len)+0))+"-"+(indices[0]%len)};//pathpointindex var p2={index:indices[1],position:[position[indices[1]*3],position[indices[1]*3+1],position[indices[1]*3+2]] ,normal:[normal[indices[1]*3],normal[indices[1]*3+1],normal[indices[1]*3+2]] ,uv:[uv[indices[1]*2],uv[indices[1]*2+1]],ppi:("2:"+(Math.round(indices[1]/len)+0))+"-"+(indices[1]%len)}; var p3={index:indices[2],position:[position[indices[2]*3],position[indices[2]*3+1],position[indices[2]*3+2]] ,normal:[normal[indices[2]*3],normal[indices[2]*3+1],normal[indices[2]*3+2]] ,uv:[uv[indices[2]*2],uv[indices[2]*2+1]],ppi:("3:"+(Math.round(indices[2]/len)+0))+"-"+(indices[2]%len)}; var po=[(p1.position[0]+p2.position[0]+p3.position[0])/3,(p1.position[1]+p2.position[1]+p3.position[1])/3,(p1.position[2]+p2.position[2]+p3.position[2])/3]; var numm=2; mesh_surface0=newland.make_tryangle(p1,p2,p3,"mesh_surface1",scene);//使用三個點繪制一個三角形 mesh_surface0.material=mat_green; mesh_surface0.sideOrientation==BABYLON.Mesh.DOUBLESIDE; mesh_surface0.layerMask=2; label_index1.isVisible=true; //label_index1.layerMask=2;//這個屬性對于gui被管對象并不生效? label_index1.text=p1.ppi;//ppi表示這個頂點是三角形的第幾個頂點,以及這個頂點位于第幾條路徑的第幾個位置 var pos1=new BABYLON.Vector3(p1.position[0],p1.position[1],p1.position[2]); label_index1.moveToVector3(pos1,scene); label_index1.itspos=pos1; label_index2.isVisible=true; label_index2.text=p2.ppi; var pos2=new BABYLON.Vector3(p2.position[0],p2.position[1],p2.position[2]); label_index2.moveToVector3(pos2,scene); label_index2.itspos=pos2; label_index3.isVisible=true; label_index3.text=p3.ppi; var pos3=new BABYLON.Vector3(p3.position[0],p3.position[1],p3.position[2]); label_index3.moveToVector3(pos3,scene); label_index3.itspos=pos3; //將三個點移動到場景的中心 pt=[(p1.position[0]-po[0])*numm,(p1.position[1]-po[1])*numm,(p1.position[2]-po[2])*numm]; p1.position=pt; pt=[(p2.position[0]-po[0])*numm,(p2.position[1]-po[1])*numm,(p2.position[2]-po[2])*numm]; p2.position=pt; pt=[(p3.position[0]-po[0])*numm,(p3.position[1]-po[1])*numm,(p3.position[2]-po[2])*numm]; p3.position=pt; //在場景的中心再繪制一個大一倍的三角形 mesh_surface=newland.make_tryangle(p1,p2,p3,"mesh_surface",scene); mesh_surface.material=mat_green; mesh_surface.sideOrientation==BABYLON.Mesh.DOUBLESIDE; mesh_surface.layerMask=1;//遮罩層次是1 } },false);
需要注意的有以下幾點:
a、鼠標指向時顯示的是這個點在arr_path中的索引,而不是在“頂點數據對象”中的索引!
b、最初的計劃是像魔獸爭霸3一樣在窗口的左下角建立一個遮罩層次為1的小視口,用來特寫被選中的三角形(mesh_surface),但是后來發現Babylon.js的GUI不能設置遮罩層次,也不能像鼠標選取一樣設置相對于哪一個相機,所以取消了左下角的小視口。
如果需要添加視口,代碼如下:
var mm = new BABYLON.FreeCamera("minimap", new BABYLON.Vector3(0,0,-20), scene); mm.layerMask = 1; var xstart = 0.0, ystart = 0.1;//意為占屏幕寬高的比例 var width = 0.2, height = 0.2; //視口邊界從左下角開始 mm.viewport = new BABYLON.Viewport( xstart, ystart, width, height ); scene.activeCameras.push(mm);var mm = new BABYLON.FreeCamera("minimap", new BABYLON.Vector3(0,0,-20), scene); mm.layerMask = 1; var xstart = 0.0, ystart = 0.1;//意為占屏幕寬高的比例 var width = 0.2, height = 0.2; //視口邊界從左下角開始 mm.viewport = new BABYLON.Viewport( xstart, ystart, width, height ); scene.activeCameras.push(mm);
c、需要注意的是GUI是一次性繪制在視口上的,默認情況下即使相關的網格位置發生變化,GUI仍然會保持在視口上最初的位置。linkWithMesh方法可以將GUI和網格綁定起來,但是似乎只能對部分“底層的”GUI類型生效,為了使得標簽跟隨頂點移動,需要手動在每一幀渲染之前更新標簽的位置:
scene.registerBeforeRender(function() { if(scene.isReady()) { if(label_index1.isVisible==true)//不斷刷新gui的位置 {//在具有多個激活相機時選擇了錯誤的參考相機?-》會強制選擇最新建立的相機 label_index1.moveToVector3(label_index1.itspos,scene); label_index2.moveToVector3(label_index2.itspos,scene); label_index3.moveToVector3(label_index3.itspos,scene); } } });
7、選取頂點:
3DsMax和Blender(Babylon.js庫在3D模型部分參考了Blender的許多設計)都使用鼠標在網格中選取需要修改的區域,我不是很習慣這種方式,所以考慮用JS函數代替鼠標操作:
var lines_inpicked={};//線段系統對象,表示所有被選中點影響的線段 var arr_ij=[];//記錄被選中的點在arr_path中的索引的數組 /*DeleteMeshes([lines_inpicked]); * PickPoints([[0,0],[0,12],[1,1]],mesh_origin) * */ //選定一些頂點 function PickPoints(arr,mesh) { if(arr_path.length==0) { alert("尚未生成路徑數組!"); return; } if(lines_inpicked.dispose) { lines_inpicked.dispose(); } //arr_ij=[]; //為了后面考慮,需要先對arr整體遍歷一遍,把首尾接口處關聯起來 arr_ij=arr;//把路徑和點的位置記錄下來 var vb=mesh.geometry._vertexBuffers; var data_pos=vb.position._buffer._data; var len_pos=data_pos.length; var data_index=mesh.geometry._indices; var len_index=data_index.length; var len=arr.length; var lines=[]; for(var i=0;i<len;i++)//對于每一個需要顯示的被選擇點 { var int0=arr[i][0]; var int1=arr[i][1]; var vec=arr_path[int0][int1];//獲取到路徑數組中的一個Vector3對象 //假設路徑數組和頂點數據是一一對應的?同時假設每一條路徑的長度都和第一條相同 var arr_index=[int0*arr_path[0].length+int1];//所有位于所選位置的頂點在頂點數據對象中的索引 //下面遍歷網格的繪制索引,找出這個被選中的頂點每一次的使用情況 for(var j=0;j<len_index;j+=3)//根據頂點在三角形中的位置分三種情況討論 {//繪制出和這個頂點相關的所有線段,這樣繪制會有很嚴重的線段重合!觀察是否對性能有很大的影響 var len2=arr_index.length; for(var k=0;k<len2;k++) { if(arr_index[k]==data_index[j])//三角形的第一個頂點 {//把這個頂點和三角形中的另兩個頂點用黃線連起來 var num2=data_index[j+1]*3; var num3=data_index[j+2]*3; var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); lines.push([vec,vec2]); lines.push([vec,vec3]); } else if(arr_index[k]==data_index[j+1])//三角形的第一個頂點 { var num2=data_index[j]*3; var num3=data_index[j+2]*3; var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); lines.push([vec,vec2]); lines.push([vec,vec3]); } else if(arr_index[k]==data_index[j+2])//三角形的第一個頂點 { var num2=data_index[j]*3; var num3=data_index[j+1]*3; var vec2=new BABYLON.Vector3(data_pos[num2],data_pos[num2+1],data_pos[num2+2]); var vec3=new BABYLON.Vector3(data_pos[num3],data_pos[num3+1],data_pos[num3+2]); lines.push([vec,vec2]); lines.push([vec,vec3]); } } } } lines_inpicked=new BABYLON.MeshBuilder.CreateLineSystem("lines_normal",{lines:lines,updatable:false},scene); lines_inpicked.color=new BABYLON.Color3(1, 1, 0); } //需要一些選擇選定頂點的方法
有以下幾處需要注意:
a、因為MakeRing生成的路徑里包括兩個重合的點(首尾),為了保證變形后的網格仍然連續,在選擇時這兩個重合的點必須同時被選中,經過考慮,規定這個確保首尾重合點同時選中的操作在生成arr數組參數時進行。
b、因為基礎網格mesh_origin的每一個頂點恰好位置不同,所以一開始我以為“與被選中的點的位置相同的點”應該具有與被選中點相同的變形效果,但是事實上經過網格變形后,完全可能出現兩個不相干的頂點位于同一位置的情況,這時應用相同的變形效果會出現錯誤,比如人可以把手放在腿上,這時手和腿的接觸點具有相同的位置,但如果因此對這兩個點應用相同的變形效果,就很詭異了。同時如前文所說,有時arr_path中存儲的位置和頂點數據中存儲的位置可能出現微小的偏差。
最后采取的方法是將arr_path中的數組轉化為頂點數據對象中的數組。
c、這里直接選取了幾個點進行變形,還需要編寫一些按照某種規則批量選取點的方法,也希望大家能幫我想一想怎樣選取頂點比較方便。
8、網格變形:
代碼如下:
/* * TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.RotationX(Math.PI/2)) *TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.Translation(0.5,0,0)) * */ //移動選定的頂點,同時更新網格、選取線、法線 function TransVertex(mesh,arr,matrix) { var len=arr.length; for(var i=0;i<len;i++)//移動路徑數組里的每個頂點 { //var vec=arr_path[arr[i][0]][arr[i][1]]; arr_path[arr[i][0]][arr[i][1]]=BABYLON.Vector3.TransformCoordinates(arr_path[arr[i][0]][arr[i][1]],matrix); } //更新網格,發現設置了closePath:true之后在頂點數據里還是會自動補上一個接續點,這還不如一開始自己添加!起碼自己添加可以保證路徑數組和頂點數據的一致性!!!! mesh=BABYLON.MeshBuilder.CreateRibbon(mesh.name,{pathArray:arr_path,updatable:true,instance:mesh,closePath:false,closeArray:false}); //上面的更新重繪是默認重算法線方向的,這與直接修改頂點數據不同 //清空法線 DeleteMeshes([lines_normal]); //更新選取頂點表示 PickPoints(arr,mesh); } //需要一些生成變化矩陣的方法
其實這里移動的頂點和前面選擇的點沒有關系,不選擇也可以直接變形,只不過不會有黃線顯示罷了。這里也同樣需要一些生成更復雜的變換矩陣的方法。
9、導出:
導出方法的調用如下:
/*ExportMesh("1",mat_blue)*/ function ExportMesh(filename,mat) { try{ newland.ExportBabylonMesh([mesh_origin],filename,mat); } catch(e) { console.error(e) } }
函數的第一個參數是導出后的文件名(不包括擴展名),第二個參數是網格使用的材質對象(暫時只支持簡單的顏色材質)。
其中ExportBabylonMesh是我編寫的newland庫中的一個方法,這個方法在前面關與3D模型的文章中也有提到過(https://www.cnblogs.com/ljzc002/p/6884252.html),這個方法整體上沒有太大的改變。但是在舊版的Babylon.js中,頂點數據對象中的data屬性是數組類型(?),而在新版中data屬性則是typedArray類型,雖然這兩種數據類型看起來很像,但在使用JSON.stringify(arr)轉化為JSON字符串時,對于同樣的數據[1,2,3],前者會轉化為"[1,2,3]"后者則是"{"0":1,"1":2,"2":3}"!
在導入模型文件時后者會報錯:“attempt to access out of range vertices in attribute 0”,這意味著在導入模型時頂點數據數組沒有正確的載入,我的解決方案是導出前將data屬性強制轉化為數組類型:
//將TypedArray轉化為普通array newland.BuffertoArray2=function(arr) { var arr2=[]; var len=arr.length; for(var i=0;i<len;i++) { arr2.push(arr[i]); } return arr2; }
對于舊版的Babylon.js應該可以不加修改的使用這個方法,因為typedArray也具有數組的大部分功能。
10、導入:
在另一個頁面里實現模型導入功能,這個頁面同樣在“基礎場景”的基礎上編寫,其中導入方法如下:
function ImportMesh(objname,filepath,filename) { BABYLON.SceneLoader.ImportMesh(objname, filepath, filename, scene , function (newMeshes, particleSystems, skeletons) {//載入完成的回調函數 if(mesh_origin&&mesh_origin.dispose) { mesh_origin.dispose(); } mesh_origin=newMeshes[0]; //mesh_origin.material=mat_frame; //mesh_origin.layerMask=2; } ); }
以上是怎么使用Chrome控制臺進行3D模型編輯的實現的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。