Vue實現圖形化積木式編程(二)

前言

前段時間想要做一個web端的圖形化積木式編程(類似少兒編程)的案例,網上沖浪了一圈又一圈,終于技術選型好,然后代碼一頓敲,終于出來了一個雛形。

TIPS:該案例設計主要參考iRobot Coding,只用做學習用途,侵刪。

https://code.irobot.com/#/

最終實現效果

最終實現效果

本文實現效果

  • 加載模型到場景中


    替換上文中的藍色正方形為babylon模型文件

完整代碼

  • 加載模型實現
<template>
  <div style="height: 100%;width: 100%;">
    <div>
      <canvas id="renderCanvas"></canvas>
    </div>
  </div>
</template>

<script>
import * as BABYLON from 'babylonjs';
import * as BABYLON_MATERAIAL from "babylonjs-materials"

async function loadScene() {
  //第一篇場景初始化,可看上一篇文章
  var scene = initScene()

  //本文內容,加載網絡模型
  await initRobot(scene)

  //開啟debug窗口
  // scene.debugLayer.show()

}

async function initRobot(scene) {
  console.log('initRobot')
  //模型url路徑
  const url = "http://localhost:8887/"
  //模型名稱
  const modelName = "sportcar.babylon"
  var result = await BABYLON.SceneLoader.ImportMeshAsync(null, url, modelName, scene);
  var meshes = result.meshes
  console.log("meshes", meshes)
  const scale = 10//縮放比例
  for (var mesh of meshes) {
    mesh.scaling = new BABYLON.Vector3(scale, scale, scale)
  }
}

function initScene() {
  //獲取到renderCanvas這個元素
  var canvas = document.getElementById("renderCanvas");
  //初始化引擎
  var engine = new BABYLON.Engine(canvas, true);
  //初始化場景
  var scene = new BABYLON.Scene(engine);
  //注冊一個渲染循環來重復渲染場景
  engine.runRenderLoop(function () {
    scene.render();
  });
  //瀏覽器窗口變化時監聽
  window.addEventListener("resize", function () {
    engine.resize();
  });

  //相機初始化
  var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 5, new BABYLON.Vector3(0, 0, 10), scene);
  camera.setPosition(new BABYLON.Vector3(20, 200, 400));
  //相機角度限制
  camera.upperBetaLimit = 1.5;//最大z軸旋轉角度差不多45度俯瞰
  camera.lowerRadiusLimit = 50;//最小縮小比例
  camera.upperRadiusLimit = 1500;//最大放大比例
  //變焦速度
  camera.wheelPrecision = 1; //電腦滾輪速度 越小靈敏度越高
  camera.pinchPrecision = 20; //手機放大縮小速度 越小靈敏度越高
  scene.activeCamera.panningSensibility = 100;//右鍵平移靈敏度
  // 將相機和畫布關聯
  camera.attachControl(canvas, true);

  //燈光初始化
  var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 10, 0), scene);
  //設置高光顏色
  light.specular = new BABYLON.Color3(0, 0, 0);
  //設置燈光強度
  light.intensity = 1

  // 綠地初始化
  var materialPlane = new BABYLON.StandardMaterial("texturePlane", scene);
  materialPlane.diffuseColor = new BABYLON.Color3(152 / 255.0, 209 / 255.0, 115 / 255.0)
  materialPlane.backFaceCulling = false;
  materialPlane.freeze()
  var plane = BABYLON.MeshBuilder.CreateDisc("ground", {radius: 3000}, scene);
  plane.rotation.x = Math.PI / 2;
  plane.material = materialPlane;
  plane.position.y = -0.1;
  plane.freezeWorldMatrix()

  //網格地板初始化
  const groundSide = 144;
  var ground = BABYLON.Mesh.CreateGround("ground", groundSide, groundSide, 1, scene, true);
  var groundMaterial = new BABYLON_MATERAIAL.GridMaterial("grid", scene);
  groundMaterial.mainColor = BABYLON.Color3.White();//底板顏色
  groundMaterial.alpha = 1;//透明度
  const gridLineGray = 0.95;
  groundMaterial.lineColor = new BABYLON.Color3(gridLineGray, gridLineGray, gridLineGray);
  groundMaterial.backFaceCulling = true; // 可看到背面
  //大網格間距
  groundMaterial.majorUnitFrequency = 16;
  //小網格間距
  groundMaterial.minorUnitVisibility = 0;
  const gridOffset = 8; // 網格偏移量
  groundMaterial.gridOffset = new BABYLON.Vector3(gridOffset, 0, gridOffset);
  groundMaterial.freeze(); // 凍結材質,優化渲染速度
  ground.material = groundMaterial
  ground.freezeWorldMatrix()

  //天空盒初始化
  var skyMaterial = new BABYLON_MATERAIAL.SkyMaterial("skyMaterial", scene);
  skyMaterial.inclination = 0
  skyMaterial.backFaceCulling = false;
  var skybox = BABYLON.Mesh.CreateBox("skyBox", 5000.0, scene);
  skybox.material = skyMaterial;

  return scene
}


export default {
  name: "test",
  data() {
    return {}
  },
  async mounted() {
    //加載場景
    await loadScene()
  },
}
</script>

<style scoped>
#renderCanvas {
  width: 680px;
  height: 680px;
  touch-action: none;
  z-index: 10000;
  border-radius: 10px;
}
</style>

babylonjs模型格式轉換與導入

0、在開源模型網上下載一個模型/自己制作一個
  • emmmm,沒有云服務器的同學可以直接跳到第2步的方案二啦!
  • 文件格式 .glTF, .glb, .obj, .babylon(實際上只想要obj/babylon格式??♂?)
  • ??上述模型都可在下方網址在線預覽模型??

https://sandbox.babylonjs.com/

  • ??3d開源素材哪里找??

https://www.zhihu.com/question/19959438

  • 筆者從這里下載了一個心愛的老爺車(有obj文件就選obj文件,其他格式的話,需要在blender等3d引擎中導出obj)??


    image.png
  • 為什么非要obj格式文件?
    為了形成規范,筆者所做的系統中模型能夠正常運行的基礎是nodes如下圖格式(每一個mesh網格都沒有父節點,不存在嵌套關系)。obj文件在線預覽就是這種規范。當然,其他導入的模型能符合這個規范也行。

  • 正確nodes結構(sandbox.babylonjs.com中打開obj文件)


    例子1

    例子2
  • 錯誤nodes結構((sandbox.babylonjs.com中打開glb文件)


    帶__root__父節點的nodes
1、轉換為.babylon文件

obj文件導入在線預覽網頁后,筆者選擇了統一把模型轉化為.babylon模型文件

  • 理論上,將obj文件拖入上方打開在線模型預覽窗口,應該出現下面模型預覽界面。


    理論上,導入obj文件后,會自動關聯mtl文件,然后mtl文件自動關聯所有圖片
  • 實際上


    導入obj文件時,無法導入文件關聯的mtl文件

    導入mtl文件時,無法導入文件關聯的圖片文件
  • 這是因為瀏覽器內核為了安全性,不能代碼加載本地資源,所以只能手動導入了。

  • 具體步驟
    1.0、準備好obj、mtl、mtl關聯的所有png圖片(不太清楚的可以用記事本打開mtl文件看一看)
    1.1、打開【https://sandbox.babylonjs.com】,選擇或拖動obj文件進去。
    1.2、拖動關聯的mtl文件進去。
    1.3、打開調試工具欄,左邊工具欄點擊展開Textures,點擊里面的每一個png,然后點擊右邊的【load Texture from file】選擇對應圖片。

    導入mtl文件關聯的材質圖片指引

1.4、按照提示紅框提示即可導出.babylon格式模型文件。導出的babylon格式,其模型結構和obj的模型結構是一致的,只不過.babylon格式模型相當于將obj、mtl、所有png都整合在一起形成一個文件了。有興趣的小伙伴可以看看左側Scene菜單,在代碼,可以遍歷該模型對象找到某個node節點,然后對這個節點進行位置變換從而到達動畫效果,或者對某個節點進行材質變換,顏色變化。

導出babylon文件格式操作指引
2、將模型文件放在服務器上
方案一(最新發現的方案)
  • Web Server for Chrome(一個谷歌瀏覽器插件),可以將本地文件搭建為一個本地HTTP服務器,插件地址??

https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb

本地http服務啟動指引
方案二
  • 由于安全原因??,無法加載本地模型[file://],只能把它放在服務器上[http(s)://]
  • 如果出現了Access-Control-Allow-Origin跨域問題,可以看看這里,改一下配置文件

https://segmentfault.com/a/1190000012550346

方案三

沒有服務器的同學,也可以使用官方提供的模型進行學習啦

https://doc.babylonjs.com/toolsAndResources/assetLibraries/availableMeshes

3、加載模型

雖然筆者使用的是obj模型結構(所有mesh都是同級沒有父節點的,不知道能不能這樣一刀切??♂?,我想表達是沒有父節點),但是這里也會有glb模型結構(帶有root父節點的mesh加載)的模型加載。

3.1、async同步形式(obj模型結構)
async function initRobot(scene) {
  console.log('initRobot')
  //模型url路徑
  const url = "http://localhost:8887/"
  //模型名稱
  const modelName = "sportcar.babylon"
  var result = await BABYLON.SceneLoader.ImportMeshAsync(null, url, modelName, scene);
  var meshes = result.meshes
  console.log("meshes", meshes)
  const scale = 10//縮放比例
  for (var mesh of meshes) {
    mesh.scaling = new BABYLON.Vector3(scale, scale, scale)
  }
}
3.2、回調的形式(obj模型結構)
function initRobot(scene) {
  console.log('initRobot')
  //模型url路徑,上傳了自己模型到服務器的同學可以將url和modelName改為對應路徑即可
  const url = "http://localhost:8887/"
  //模型名稱
  const modelName = "sportcar.babylon"
  BABYLON.SceneLoader.ImportMesh("", url, modelName, scene, function (meshes) {
    console.log(meshes)
    const scale = 10//縮放比例
    for (var mesh of meshes) {
      mesh.scaling = new BABYLON.Vector3(scale, scale, scale)
    }
  });
}
3.3、async同步形式(glb模型結構)
async function initRobot(scene) {
  console.log('initRobot')
  //模型url路徑,上傳了自己模型到服務器的同學可以將url和modelName改為對應路徑即可
  const url = "https://models.babylonjs.com/"
  //模型名稱
  const modelName = "toast_acrobatics.glb"
  var result = await BABYLON.SceneLoader.ImportMeshAsync(null, url, modelName, scene);
  var meshes = result.meshes
  console.log(meshes)
  var scale = 100//縮放比例
  //只對__root__節點進行操作
  meshes[0].scaling = new BABYLON.Vector3(scale, scale, scale)
  meshes[0].position._x += 24
  // scene.createDefaultEnvironment()
}
3.4、回調的形式(glb模型結構)
function initRobot(scene) {
  console.log('initRobot')
  //模型url路徑,上傳了自己模型到服務器的同學可以將url和modelName改為對應路徑即可
  const url = "https://models.babylonjs.com/"
  //模型名稱
  const modelName = "toast_acrobatics.glb"
  BABYLON.SceneLoader.ImportMesh("", url, modelName, scene, function (meshes) {
    scene.createDefaultEnvironment()
    console.log(meshes)
    var scale = 100//縮放比例
    //只對__root__節點進行操作
    meshes[0].scaling = new BABYLON.Vector3(scale, scale, scale)
    meshes[0].position._x += 24
    // scene.createDefaultEnvironment()
  });
}
  • 加載很慢的話,可以f12開發者工具network看看是不是模型還在下載,模型很大,你要忍一下??
  • 可以發現meshes打印出來數據列表(列表中每個t對象的name)和本文第1點中在線查看模型左側Scene菜單中的nodes列表中節點名稱是一致的
問題:導入模型后一片黑
  • 解決方案:
scene.createDefaultEnvironment()
4、(額外)啟用調試窗口
scene.debugLayer.show()
  • 是不是看到了熟悉的窗口

后續計劃

Babylon.js

  • 點擊移動物體
  • 自定義啟動界面
  • 物體重力效果
  • babylonjs-gui 按鈕實現
  • babylonjs+ammojs 碰撞體實現
  • 將3d界面放入可拖動窗口中

Blockly

  • 入門使用blockly
  • 自定義block塊
  • blockly第三方組件使用
  • 接入js-interpreter,步驟運行block塊
  • ......(想到啥寫啥)

開源項目GitHub鏈接

https://github.com/Wenbile/Child-Programming-Web

你的點贊是我繼續編寫的動力

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容