Maison > interface Web > Tutoriel H5 > Implémentation de l'utilisation de la console Chrome pour l'édition de modèles 3D (code)

Implémentation de l'utilisation de la console Chrome pour l'édition de modèles 3D (code)

不言
Libérer: 2018-07-26 09:50:35
original
3554 Les gens l'ont consulté

Cet article partage avec vous l'implémentation (code) de l'utilisation de la console Chrome pour l'édition de modèles 3D. Le contenu est très détaillé. Les amis dans le besoin peuvent s'y référer. J'espère qu'il pourra vous aider.

Avant-propos : Le cœur de l'édition de modèles 3D est l'édition des positions des sommets et des couleurs de texture. Le but de cette recherche est de trouver une méthode pour éditer directement le modèle via la programmation. Cette méthode d'édition est similaire à la méthode populaire du clic de souris. , la relation entre les méthodes d'édition par glisser-déposer est très similaire à la relation entre « les programmeurs écrivent des pages Web statiques » et « les artistes coupent des pages Web » dans la programmation frontale.

1. Utilisation de l'outil :

1. Visitez https://ljzc002.github.io/SnowMiku/HTML/MakeRibbon.html pour ouvrir la page du générateur de grille de bandes

Il y a une boule verte à (0,-10,0), (0,0,0), (0,10,0) du système de coordonnées du monde de la scène comme point de référence. , faites glisser le curseur vers le haut, le bas, la gauche, la droite et la souris pour parcourir la scène.

2. Appuyez sur la touche F12 pour ouvrir la console Chrome, saisissez dans la console : MakeRibbon(MakeRing(5,12),-10,2,11,"mesh_ribbon") et appuyez sur Entrée :

Une surface cylindrique avec un rayon de 5, une subdivision de surface de 12, une extrémité gauche à -10, une distance de 2 entre deux anneaux et un total de 11 anneaux est dessiné dans la scène.

Zoom avant :

3. Entrez ShowNormals(mesh_origin) pour afficher la direction normale de chaque sommet avec un segment de ligne rouge

Entrez DeleteMeshes([lines_normal]) pour supprimer toutes les normales, et entrez DeleteMeshes([mesh_origin]) pour supprimer le maillage cylindrique.

4. Déplacez la souris dans un triangle de la grille, et les informations sur le sommet du triangle seront affichées :

où "1:2- 5" signifie qu'il s'agit d'un triangle. Le premier sommet de , ce sommet est situé sur l'anneau d'indice 2 (le troisième anneau), et l'indice de ce sommet dans l'anneau est 5 (c'est-à-dire le sixième sommet).

5. Entrez PickPoints([[2,5],[3,5],[2,6]],mesh_origin) pour sélectionner ces sommets

Toutes les lignes de bordure affectées par les sommets sélectionnés sont marquées en jaune. Cette "sélection" ne change que l'apparence.

6. Entrez TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.Translation(0,0,-10)) pour déplacer le sommet sélectionné 10 dans la direction négative de l'axe z et du sommet déplacé. précédemment sélectionné Les sommets n'ont pas d'importance, arr_ij peut également être directement remplacé par le tableau d'index [[2,5],[3,5],[2,6]].

Un autre type de déformation peut être obtenu en saisissant : TransVertex(mesh_origin,arr_ij,BABYLON.Matrix.RotationX(Math.PI/2)), qui peut transformer le Le sommet pivote de 90 degrés autour de X.

Entrez DeleteMeshes([lines_inpicked]) pour annuler l'effet sélectionné, entrez ChangeMaterial(mesh_origin,mat_blue) pour changer la bordure en une texture bleue :

Oui Après avoir vu l'effet déformé, vous pouvez continuer à sélectionner les sommets et les déformer

7. Entrez ExportMesh("1",mat_blue) et exportez le fichier modèle Babylon au format txt, avec le nom de fichier "1". .txt"

8. Renommez le txt exporté en 9.babylon et placez-le dans le répertoire du site Web. Visitez https://ljzc002.github.io/SnowMiku/HTML /LoadBabylon. page d'aperçu du modèle html, entrez ImportMesh("","../ASSETS/SCENE/","9.babylon") dans la console pour charger le modèle qui vient d'être exporté.

2. Idées de programmation :

1. Tout d'abord, établissez une scène de base qui peut être utilisée pour divers tests :

<!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>
Copier après la connexion

Ceci. La scène 3D comprend Certains éléments de base requis pour des tests simples sont utilisés ici. La version non compressée de la bibliothèque Babylon.js contenant tous les composants est utilisée. Afin d'économiser de la bande passante lors de l'utilisation réelle, vous pouvez utiliser les outils fournis par le responsable Babylon.js. site Web pour personnaliser la version simplifiée ou compressée. Bibliothèque Babylon.js

2. Construire un maillage de base

Prévoyez de générer une variété de modèles simples en effectuant des transformations de sommets sur un maillage de base, dans Babylon. .js" "Strip" est un type de maillage très adapté à la transformation de sommets. Le didacticiel officiel Babylon.js contient de la documentation sur la construction et la déformation des bandes. Vous pouvez télécharger la traduction chinoise ici http://down.51cto.com/data /2449757 .

Le code utilisé pour établir la grille de base est le suivant :

//下面这些函数都通过控制台调用
    //在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;
    }
Copier après la connexion

Plusieurs problèmes rencontrés en programmation :

a. sont en réalité différents. Il peut y avoir de légers changements dans l'affichage, par exemple, 5 peut devenir 4,999999999999999999999, mais cela semble n'avoir aucun effet.

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;
     }
Copier après la connexion

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);
    }
Copier après la connexion

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=[];
    }
Copier après la connexion

值得注意的是,直接在控制台里执行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);
Copier après la connexion

需要注意的有以下几点:

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);
Copier après la connexion

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);
                }
            }
        });
Copier après la connexion

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);
    }
    //需要一些选择选定顶点的方法
Copier après la connexion

有以下几处需要注意:

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);
    }
    //需要一些生成变化矩阵的方法
Copier après la connexion

其实这里移动的顶点和前面选择的点没有关系,不选择也可以直接变形,只不过不会有黄线显示罢了。这里也同样需要一些生成更复杂的变换矩阵的方法。

9、导出:

导出方法的调用如下:

/*ExportMesh("1",mat_blue)*/
    function ExportMesh(filename,mat)
    {
        try{
            newland.ExportBabylonMesh([mesh_origin],filename,mat);
        }
        catch(e)
        {
            console.error(e)
        }
    }
Copier après la connexion

函数的第一个参数是导出后的文件名(不包括扩展名),第二个参数是网格使用的材质对象(暂时只支持简单的颜色材质)。

其中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;
}
Copier après la connexion

对于旧版的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;
                }
        );
    }
Copier après la connexion

相关推荐:

Storage Event如何实现页面间通信

 关于html5中标签video播放的新解析

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal