Heim > Artikel > Web-Frontend > Implementierung der Verwendung der Chrome-Konsole für die Bearbeitung von 3D-Modellen (Code)
In diesem Artikel erfahren Sie, wie Sie die Chrome-Konsole für die Bearbeitung von 3D-Modellen verwenden. Ich hoffe, dass er Ihnen weiterhelfen kann 🎜>Vorwort: Der Kern der 3D-Modellbearbeitung ist die Bearbeitung von Scheitelpunktpositionen und Texturfarben. Der Zweck dieser Forschung besteht darin, eine Methode zur direkten Bearbeitung des Modells durch Programmierung zu finden. Diese Bearbeitungsmethode ähnelt der beliebten Mausklickmethode. Die Beziehung zwischen Drag-and-Drop-Bearbeitungsmethoden ist der Beziehung zwischen „Programmierer schreiben statische Webseiten“ und „Künstler schneiden Webseiten“ in der Front-End-Programmierung sehr ähnlich.
1. Werkzeugverwendung:
1. Besuchen Sie https://ljzc002.github.io/SnowMiku/HTML/MakeRibbon.html, um die Seite des Streifengittergenerators zu öffnen
Es gibt einen grünen Ball bei (0,-10,0), (0,0,0), (0,10,0) des Szenenweltkoordinatensystems als Referenzpunkt. Verwenden Sie die Tasten „Nach oben“, „Nach unten“, „Links“, „Rechts“ und Ziehen mit der Maus, um durch die Szene zu navigieren.
2. Drücken Sie die Taste F12, um die Chrome-Konsole zu öffnen, geben Sie in die Konsole ein: MakeRibbon(MakeRing(5,12),-10,2,11,"mesh_ribbon") und drücken Sie die Eingabetaste:
Eine zylindrische Oberfläche mit einem Radius von 5, einer Oberflächenunterteilung von 12, einem linken Ende bei -10, einem Abstand von 2 zwischen jeweils zwei Ringen und insgesamt 11 Ringen in die Szene gezeichnet.
Vergrößern:
3. Geben Sie ShowNormals(mesh_origin) ein, um die normale Richtung jedes Scheitelpunkts mit einem roten Liniensegment anzuzeigen
Geben Sie „DeleteMeshes([lines_normal])“ ein, um alle Normalen zu löschen, und geben Sie „DeleteMeshes([mesh_origin])“ ein, um das zylindrische Netz zu löschen.
4. Bewegen Sie die Maus in ein Dreieck im Raster und die Scheitelpunktinformationen des Dreiecks werden angezeigt:
wobei „1:2- 5" bedeutet, dass dies ein Dreieck ist. Der erste Scheitelpunkt von befindet sich auf dem Ring mit Index 2 (dem dritten Ring), und der Index dieses Scheitelpunkts im Ring ist 5 (d. h. der sechste Scheitelpunkt).
5. Geben Sie PickPoints([[2,5],[3,5],[2,6]],mesh_origin) ein, um diese Eckpunkte auszuwählen
Alle von den ausgewählten Eckpunkten betroffenen Grenzlinien werden gelb markiert. Durch diese „Auswahl“ ändert sich nur das Erscheinungsbild.
6. Geben Sie TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.Translation(0,0,-10)) ein, um den ausgewählten Scheitelpunkt 10 in die negative Richtung der Z-Achse zu verschieben zuvor ausgewählt Die Scheitelpunkte spielen eigentlich keine Rolle, arr_ij kann auch direkt durch das Indexarray [[2,5],[3,5],[2,6] ersetzt werden.
Eine andere Art der Verformung kann durch Eingabe von TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.RotationX(Math.PI/2)) erreicht werden, wodurch die Auswahl transformiert werden kann Der Scheitelpunkt wird um 90 Grad um X gedreht.
Geben Sie DeleteMeshes([lines_inpicked]) ein, um den ausgewählten Effekt abzubrechen, und geben Sie ChangeMaterial(mesh_origin,mat_blue) ein, um den Rand in eine blaue Textur zu ändern:
Ja Nachdem Sie den deformierten Effekt gesehen haben, können Sie mit der Auswahl der Scheitelpunkte und deren Verformung fortfahren
7. Geben Sie ExportMesh("1",mat_blue) ein und exportieren Sie die Babylon-Modelldatei im TXT-Format mit dem Dateinamen „1 .txt"
8. Benennen Sie die exportierte TXT-Datei in 9.babylon um und legen Sie sie im Website-Verzeichnis ab. Besuchen Sie https://ljzc002.github.io/SnowMiku/HTML /LoadBabylon. HTML-Modellvorschauseite, geben Sie ImportMesh("","../ASSETS/SCENE/","9.babylon") in die Konsole ein, um das gerade exportierte Modell zu laden.
2. Programmierideen:
Erstellen Sie zunächst eine Basisszene, die für verschiedene Tests verwendet werden kann:
<!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>
Diese 3D-Szene Hier werden einige grundlegende Elemente verwendet, die zum Testen erforderlich sind. Die unkomprimierte Version der Babylon.js-Bibliothek enthält alle Komponenten. Um bei der tatsächlichen Verwendung Bandbreite zu sparen, können Sie die auf der offiziellen Website von Babylon.js bereitgestellten Tools verwenden eine optimierte oder komprimierte Version der Babylon .js-Bibliothek
2. Erstellen Sie ein Basisnetz
Planen Sie die Generierung verschiedener einfacher Modelle durch die Durchführung einer Scheitelpunkttransformation auf einem Basisnetz, „Strips“ in Babylon. js“ ist ein Netztyp, der sich sehr gut für die Scheitelpunkttransformation eignet. Dokumente zur Streifenkonstruktion und -verformung finden Sie im offiziellen Babylon.js-Tutorial. Die chinesische Übersetzung können Sie hier herunterladen http://down.51cto.com/data/2449757 .
Der zum Erstellen des Grundrasters verwendete Code lautet wie folgt:
//下面这些函数都通过控制台调用 //在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; }
Bei der Programmierung sind mehrere Probleme aufgetreten:
a Die im Pfad festgelegten Koordinatenwerte sind tatsächlich Wird angezeigt, wenn kleine Änderungen auftreten können, z. B. 5 kann zu 4,99999999999999999999 werden, aber es scheint keine Auswirkung zu haben.
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; } ); }
相关推荐:
Das obige ist der detaillierte Inhalt vonImplementierung der Verwendung der Chrome-Konsole für die Bearbeitung von 3D-Modellen (Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!