Bagikan melalui


Tutorial: Membangun model piano 3D

Dalam tutorial sebelumnya dalam seri ini, kami telah menyiapkan halaman web yang berisi adegan Babylon.js dengan kamera dan lampu. Dalam tutorial ini, kita akan membangun dan menambahkan model piano ke dalam adegan.

Standup Piano Mesh

Dalam tutorial ini, Anda akan belajar cara:

  • Membuat, memposisikan, dan menggabungkan jala
  • Membangun keyboard piano dari jala kotak
  • Mengimpor model 3D dari bingkai piano

Sebelum Anda mulai

Pastikan Anda telah melalui tutorial sebelumnya dalam seri dan siap untuk terus menambahkan ke kode.

index.html

<html>
    <head>
        <title>Piano in BabylonJS</title>
        <script src="https://cdn.babylonjs.com/babylon.js"></script>
        <script src="scene.js"></script>
        <style>
            body,#renderCanvas { width: 100%; height: 100%;}
        </style>
    </head>
    <body>
        <canvas id="renderCanvas"></canvas>
        <script type="text/javascript">
            const canvas = document.getElementById("renderCanvas");
            const engine = new BABYLON.Engine(canvas, true); 

            createScene(engine).then(sceneToRender => {
                engine.runRenderLoop(() => sceneToRender.render());
            });
            
            // Watch for browser/canvas resize events
            window.addEventListener("resize", function () {
                engine.resize();
            });
        </script>
    </body>
</html>

scene.js

const createScene = async function(engine) {
    const scene = new BABYLON.Scene(engine);

    const alpha =  3*Math.PI/2;
    const beta = Math.PI/50;
    const radius = 220;
    const target = new BABYLON.Vector3(0, 0, 0);
    
    const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
    camera.attachControl(canvas, true);
    
    const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
    light.intensity = 0.6;

    const xrHelper = await scene.createDefaultXRExperienceAsync();

    return scene;
}

Memulai

Mari kita mulai dengan membuat keyboard piano sederhana yang memiliki struktur ini:

Deskripsi register piano

Dalam gambar ini, ada 7 kunci putih dan 5 kunci hitam, masing-masing diberi label dengan nama catatan. Keyboard piano 88-key lengkap berisi 7 pengulangan penuh dari pilihan tombol ini (juga disebut register) dan 4 tombol tambahan. Setiap register memiliki frekuensi ganda dari register sebelumnya. Misalnya, frekuensi pitch C5 (yang berarti catatan C di register kelima) dua kali lipat dari C4, frekuensi pitch D5 dua kali lipat dari D4, dan sebagainya.

Secara visual, setiap register terlihat persis sama dengan yang lain, sehingga kita dapat mulai dengan menyelidiki cara membuat keyboard piano sederhana dengan pilihan kunci ini. Nantinya, kita dapat menemukan cara untuk memperluas cakupan ke keyboard piano lengkap 88-key.

Membangun keyboard piano sederhana

Catatan

Meskipun dimungkinkan untuk menemukan model keyboard piano 3D yang dibuat sebelumnya dari sumber online dan mengimpornya ke halaman web kami, kami akan membangun keyboard dari awal dalam tutorial ini untuk memungkinkan penyesuaian maksimum dan untuk menampilkan bagaimana model 3D dapat dibuat melalui Babylon.js.

  1. Sebelum kita mulai membuat jala untuk membangun keyboard, perhatikan bahwa setiap tombol hitam tidak selaras dengan sempurna di tengah-tengah dua tombol putih di sekitarnya, dan tidak setiap tombol memiliki lebar yang sama. Ini berarti bahwa kita harus membuat dan memposisikan jala untuk setiap kunci satu per satu.

    Perataan Kunci Hitam

  2. Untuk kunci putih, kita dapat melakukan pengamatan bahwa setiap kunci putih terdiri dari dua bagian: (1) bagian bawah di bawah kunci hitam dan (2) bagian atas di samping kunci hitam. Kedua bagian memiliki dimensi yang berbeda tetapi ditumpuk bersama-sama untuk mengkremasi kunci putih penuh.

    Bentuk Kunci Putih

  3. Berikut adalah kode untuk membuat satu kunci putih untuk catatan C (jangan khawatir tentang menambahkannya ke dalamscene.js belum):

    const whiteKeyBottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: 2.3, height: 1.5, depth: 4.5}, scene);
    const whiteKeyTop = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: 1.4, height: 1.5, depth: 5}, scene);
    whiteKeyTop.position.z += 4.75;
    whiteKeyTop.position.x -= 0.45;
    
    // Parameters of BABYLON.Mesh.MergeMeshes:
    // (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
    const whiteKeyV1 = BABYLON.Mesh.MergeMeshes([whiteKeyBottom, whiteKeyTop], true, false, null, false, false);
    whiteKeyV1.material = whiteMat;
    whiteKeyV1.name = "C4";
    

    Di sini kita membuat dua jala Kotak , satu untuk bagian bawah dan satu untuk bagian atas kunci putih. Kami kemudian memodifikasi posisi bagian atas untuk menumpuknya di atas bagian bawah dan memindahkannya ke arah kiri untuk meninggalkan ruang bagi kunci hitam tetangga (C#).

    Akhirnya, kedua bagian ini digabungkan menggunakan fungsi MergeMeshes untuk menjadi satu kunci putih lengkap. Ini adalah jala yang dihasilkan yang akan dihasilkan kode ini:

    Kunci Putih C

  4. Membuat kunci hitam lebih sederhana. Karena semua kunci hitam berbentuk kotak, kita dapat membuat kunci hitam hanya dengan membuat jala kotak dengan StandardMaterial berwarna hitam.

    Catatan

    Karena warna jala default adalah abu-abu muda yang menyerupai putih, tutorial ini tidak menyertakan langkah-langkah untuk menambahkan bahan warna putih ke tombol putih. Namun, jangan ragu untuk menambahkan bahan sendiri jika Anda ingin warna putih yang benar dan cerah pada tombol putih.

    Berikut adalah kode untuk membuat kunci hitam C# (jangan khawatir tentang menambahkan ini ke scene.js juga):

    const blackMat = new BABYLON.StandardMaterial("black");
    blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
    const blackKey = BABYLON.MeshBuilder.CreateBox("C#4", {width: 1.4, height: 2, depth: 5}, scene);
    blackKey.position.z += 4.75;
    blackKey.position.y += 0.25;
    blackKey.position.x += 0.95;
    blackKey.material = blackMat;
    

    Kunci hitam yang dihasilkan oleh kode ini (bersama dengan kunci putih sebelumnya) akan terlihat seperti ini:

    Kunci Hitam C#

  5. Seperti yang Anda lihat, membuat setiap kunci dapat menghasilkan banyak kode serupa karena kita harus menentukan masing-masing dimensi dan posisinya. Mari kita coba membuat proses pembuatan lebih efisien di bagian berikutnya.

Membangun keyboard piano sederhana secara efisien

  1. Meskipun setiap tombol putih memiliki bentuk yang sedikit berbeda satu sama lain, semuanya dapat dibuat dengan menggabungkan bagian atas dan bagian bawah. Mari kita terapkan fungsi generik untuk membuat dan memposisikan kunci putih apa pun.

    Tambahkan fungsi di bawah ini ke scene.js, di createScene() luar fungsi :

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
    }
    

    Dalam blok kode ini, kami membuat fungsi bernama buildKey(), yang membangun dan mengembalikan kunci putih jika props.type adalah "white". Dengan mengidentifikasi jenis kunci dalam parameter props, kita dapat membuat kunci hitam dan kunci putih dalam fungsi yang sama dengan mencabangkan menggunakan pernyataan if.

    Parameternya buildKey() adalah:

    • scene: adegan tempat kunci berada
    • induk: induk jala (ini memungkinkan kita untuk mengelompokkan semua kunci bersama-sama ke satu induk)
    • props: properti kunci yang akan dibangun

    props untuk kunci putih akan berisi item berikut:

    • type: "white"
    • nama: nama catatan yang diwakili kunci
    • topWidth: lebar bagian atas
    • bottomWidth: lebar bagian bawah
    • topPositionX: posisi x bagian atas relatif terhadap bagian bawah
    • wholePositionX: x-position dari seluruh kunci relatif terhadap titik akhir register (tepi kanan kunci B).
    • register: daftarkan bahwa kunci tersebut milik (angka antara 0 dan 8)
    • referencePositionX: koordinat x titik akhir register (digunakan sebagai titik referensi).

    Dengan memisahkan wholePositionX dan referencePositionX, kita dapat menginisialisasi props parameter yang diperlukan untuk membuat jenis kunci tertentu (misalnya C) dalam register apa pun, dan kemudian menambahkan dan registerreferencePositionX ke saat membuat kunci tersebut props dalam register tertentu (misalnya C4, C5).

  2. Demikian pula, kita juga dapat menulis fungsi generik untuk membuat kunci hitam. Mari kita perluas fungsi untuk menyertakan logika tersebut buildKey() :

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
        else if (props.type === "black") {
            /*
            Props for building a black key should contain: 
            note, wholePositionX, register, referencePositionX
    
            As an example, the props for building the C#4 black key would be
            {type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
            */
    
            // Create black color material
            const blackMat = new BABYLON.StandardMaterial("black");
            blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
            // Create black key
            const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
            key.position.z += 4.75;
            key.position.y += 0.25;
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.material = blackMat;
            key.parent = parent;
    
            return key;
        }
    }
    

    props untuk kunci hitam berisi item berikut:

    • jenis: "hitam"
    • nama: nama catatan yang diwakili kunci
    • wholePositionX: x-position dari seluruh kunci relatif terhadap titik akhir register (tepi kanan kunci B)
    • register: daftarkan bahwa kunci tersebut milik (angka antara 0 dan 8)
    • referencePositionX: koordinat x titik akhir register (digunakan sebagai titik referensi).

    props untuk membuat kunci hitam jauh lebih sederhana karena membuat kunci hitam hanya melibatkan pembuatan kotak, dan lebar dan posisi z setiap kunci hitam sama.

  3. Sekarang kita memiliki cara yang lebih efisien untuk membuat kunci, mari kita inisialisasi props array yang menyimpan untuk setiap kunci yang sesuai dengan catatan dalam register, dan kemudian memanggil buildKey() fungsi dengan masing-masing dari mereka untuk membuat keyboard sederhana di register ke-4.

    Kami juga akan membuat TransformNode bernama keyboard untuk bertindak sebagai induk dari semua kunci piano. Karena setiap perubahan posisi atau penskalakan yang diterapkan pada induk juga akan diterapkan pada anak-anak, mengelompokkan kunci dengan cara ini akan memungkinkan kita untuk menskalakan atau memindahkannya secara keseluruhan.

    Tambahkan baris kode berikut dalam createScene() fungsi :

    const keyParams = [
        {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
        {type: "black", note: "C#", wholePositionX: -13.45},
        {type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
        {type: "black", note: "D#", wholePositionX: -10.6},
        {type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
        {type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
        {type: "black", note: "F#", wholePositionX: -6.35},
        {type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
        {type: "black", note: "G#", wholePositionX: -3.6},
        {type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
        {type: "black", note: "A#", wholePositionX: -0.85},
        {type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
    ]
    
    // Transform Node that acts as the parent of all piano keys
    const keyboard = new BABYLON.TransformNode("keyboard");
    
    keyParams.forEach(key => {
        buildKey(scene, keyboard, Object.assign({register: 4, referencePositionX: 0}, key));
    })
    

    Seperti yang mungkin telah Anda perhatikan, dalam blok kode ini kami menempatkan semua kunci relatif terhadap asal ruang.

  4. Berikut adalah kode yang scene.js berisi sejauh ini:

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
        else if (props.type === "black") {
            /*
            Props for building a black key should contain: 
            note, wholePositionX, register, referencePositionX
    
            As an example, the props for building the C#4 black key would be
            {type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
            */
    
            // Create black color material
            const blackMat = new BABYLON.StandardMaterial("black");
            blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
            // Create black key
            const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
            key.position.z += 4.75;
            key.position.y += 0.25;
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.material = blackMat;
            key.parent = parent;
    
            return key;
        }
    }
    
    const createScene = async function(engine) {
        const scene = new BABYLON.Scene(engine);
    
        const alpha =  3*Math.PI/2;
        const beta = Math.PI/50;
        const radius = 220;
        const target = new BABYLON.Vector3(0, 0, 0);
    
        const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
        camera.attachControl(canvas, true);
    
        const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
        light.intensity = 0.6;
    
        const keyParams = [
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
            {type: "black", note: "C#", wholePositionX: -13.45},
            {type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
            {type: "black", note: "D#", wholePositionX: -10.6},
            {type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
            {type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
            {type: "black", note: "F#", wholePositionX: -6.35},
            {type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
            {type: "black", note: "G#", wholePositionX: -3.6},
            {type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
            {type: "black", note: "A#", wholePositionX: -0.85},
            {type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
        ]
    
        // Transform Node that acts as the parent of all piano keys
        const keyboard = new BABYLON.TransformNode("keyboard");
    
        keyParams.forEach(key => {
            buildKey(scene, keyboard, Object.assign({register: 4, referencePositionX: 0}, key));
        })
    
        const xrHelper = await scene.createDefaultXRExperienceAsync();
    
        return scene;
    }
    
  5. Seperti inilah tampilan keyboard yang dihasilkan:

    Keyboard Piano dengan Satu Register

Memperluas ke piano 88-key

Di bagian ini, mari kita perluas penggunaan fungsi pembuatan kunci untuk menghasilkan keyboard piano 88 tombol penuh.

  1. Seperti disebutkan sebelumnya, keyboard piano 88 tombol penuh berisi 7 register berulang dan 4 catatan lainnya. 3 dari catatan tambahan tersebut ada di register 0 (ujung kiri keyboard), dan 1 ada di register 8 (ujung kanan keyboard).

    Tata letak piano 88 kunci

  2. Pertama-tama kami akan bekerja membangun 7 pengulangan penuh dengan menambahkan perulangan tambahan di sekitar perulangan yang kami tulis sebelumnya. Ganti perulangan sebelumnya untuk buildKey() fungsi dengan kode berikut:

    // Register 1 through 7
    var referencePositionX = -2.4*14;
    for (let register = 1; register <= 7; register++) {
        keyParams.forEach(key => {
            buildKey(scene, keyboard, Object.assign({register: register, referencePositionX: referencePositionX}, key));
        })
        referencePositionX += 2.4*7;
    }
    

    Dalam perulangan ini, kami membangun kunci untuk register 1 sampai 7 dan meningkatkan posisi referensi setiap kali kami beralih ke register berikutnya.

  3. Selanjutnya, mari kita buat kunci lainnya. Tambahkan cuplikan berikut ke createScene() fungsi :

    // Register 0
    buildKey(scene, keyboard, {type: "white", note: "A", topWidth: 1.9, bottomWidth: 2.3, topPositionX: -0.20, wholePositionX: -2.4, register: 0, referencePositionX: -2.4*21});
    keyParams.slice(10, 12).forEach(key => {
        buildKey(scene, keyboard, Object.assign({register: 0, referencePositionX: -2.4*21}, key));
    })
    
    // Register 8
    buildKey(scene, keyboard, {type: "white", note: "C", topWidth: 2.3, bottomWidth: 2.3, topPositionX: 0, wholePositionX: -2.4*6, register: 8, referencePositionX: 84});
    

    Perhatikan bahwa kunci paling kiri dan kunci paling kanan keyboard piano tidak cocok dengan dimensi alat peraga yang ditentukan di keyParams (karena tidak di samping tombol hitam di tepi), jadi kita perlu menentukan objek baru props bagi masing-masing dari mereka untuk menentukan bentuk khusus mereka.

  4. Keyboard yang dihasilkan akan terlihat seperti ini setelah perubahan dilakukan:

    Mesh Keyboard Piano Penuh

Menambahkan bingkai piano

  1. Adegan terlihat sedikit aneh hanya dengan keyboard mengambang di ruang. Mari kita tambahkan bingkai piano di sekitar keyboard untuk membuat tampilan piano standup.

  2. Mirip dengan cara kita membuat kunci, kita juga dapat membuat bingkai dengan memposisikan dan menggabungkan sekelompok jala kotak.

    Namun, kami akan meninggalkan tantangan itu bagi Anda untuk mencoba sendiri dan menggunakan BABYLON. SceneLoader.ImportMesh untuk mengimpor jala buatan sebelumnya dari bingkai piano standup. Tambahkan bagian kode ini ke createScene():

    // Transform node that acts as the parent of all piano components
    const piano = new BABYLON.TransformNode("piano");
    keyboard.parent = piano;
    
    // Import and scale piano frame
    BABYLON.SceneLoader.ImportMesh("frame", "https://raw.githubusercontent.com/MicrosoftDocs/mixed-reality/docs/mixed-reality-docs/mr-dev-docs/develop/javascript/tutorials/babylonjs-webxr-piano/files/", "pianoFrame.babylon", scene, function(meshes) {
        const frame = meshes[0];
        frame.parent = piano;
    });
    

    Perhatikan bahwa kita, sekali lagi, membuat induk TransformNode bernama piano untuk mengelompokkan keyboard dan bingkai bersama-sama secara keseluruhan. Ini akan membuat bergerak atau menskalakan seluruh piano jauh lebih mudah jika kita perlu melakukannya.

  3. Setelah bingkai diimpor, perhatikan bahwa keyboard berbaring di bagian bawah bingkai (karena koordinat y tombol berada di 0 secara default). Mari angkat keyboard agar pas dengan bingkai piano standup:

    // Lift piano keys
    keyboard.position.y += 80;
    

    Karena keyboard adalah induk dari semua kunci piano, kita dapat mengangkat semua kunci piano hanya dengan mengubah posisi keyboardy .

  4. Kode akhir scene.js akan terlihat seperti ini:

    const buildKey = function (scene, parent, props) {
        if (props.type === "white") {
            /*
            Props for building a white key should contain: 
            note, topWidth, bottomWidth, topPositionX, wholePositionX, register, referencePositionX
    
            As an example, the props for building the middle C white key would be
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4, register: 4, referencePositionX: 0}
            */
    
            // Create bottom part
            const bottom = BABYLON.MeshBuilder.CreateBox("whiteKeyBottom", {width: props.bottomWidth, height: 1.5, depth: 4.5}, scene);
    
            // Create top part
            const top = BABYLON.MeshBuilder.CreateBox("whiteKeyTop", {width: props.topWidth, height: 1.5, depth: 5}, scene);
            top.position.z =  4.75;
            top.position.x += props.topPositionX;
    
            // Merge bottom and top parts
            // Parameters of BABYLON.Mesh.MergeMeshes: (arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials)
            const key = BABYLON.Mesh.MergeMeshes([bottom, top], true, false, null, false, false);
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.name = props.note + props.register;
            key.parent = parent;
    
            return key;
        }
        else if (props.type === "black") {
            /*
            Props for building a black key should contain: 
            note, wholePositionX, register, referencePositionX
    
            As an example, the props for building the C#4 black key would be
            {type: "black", note: "C#", wholePositionX: -13.45, register: 4, referencePositionX: 0}
            */
    
            // Create black color material
            const blackMat = new BABYLON.StandardMaterial("black");
            blackMat.diffuseColor = new BABYLON.Color3(0, 0, 0);
    
            // Create black key
            const key = BABYLON.MeshBuilder.CreateBox(props.note + props.register, {width: 1.4, height: 2, depth: 5}, scene);
            key.position.z += 4.75;
            key.position.y += 0.25;
            key.position.x = props.referencePositionX + props.wholePositionX;
            key.material = blackMat;
            key.parent = parent;
    
            return key;
        }
    }
    
    const createScene = async function(engine) {
        const scene = new BABYLON.Scene(engine);
    
        const alpha =  3*Math.PI/2;
        const beta = Math.PI/50;
        const radius = 220;
        const target = new BABYLON.Vector3(0, 0, 0);
    
        const camera = new BABYLON.ArcRotateCamera("Camera", alpha, beta, radius, target, scene);
        camera.attachControl(canvas, true);
    
        const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
        light.intensity = 0.6;
    
        const keyParams = [
            {type: "white", note: "C", topWidth: 1.4, bottomWidth: 2.3, topPositionX: -0.45, wholePositionX: -14.4},
            {type: "black", note: "C#", wholePositionX: -13.45},
            {type: "white", note: "D", topWidth: 1.4, bottomWidth: 2.4, topPositionX: 0, wholePositionX: -12},
            {type: "black", note: "D#", wholePositionX: -10.6},
            {type: "white", note: "E", topWidth: 1.4, bottomWidth: 2.3, topPositionX: 0.45, wholePositionX: -9.6},
            {type: "white", note: "F", topWidth: 1.3, bottomWidth: 2.4, topPositionX: -0.55, wholePositionX: -7.2},
            {type: "black", note: "F#", wholePositionX: -6.35},
            {type: "white", note: "G", topWidth: 1.3, bottomWidth: 2.3, topPositionX: -0.2, wholePositionX: -4.8},
            {type: "black", note: "G#", wholePositionX: -3.6},
            {type: "white", note: "A", topWidth: 1.3, bottomWidth: 2.3, topPositionX: 0.2, wholePositionX: -2.4},
            {type: "black", note: "A#", wholePositionX: -0.85},
            {type: "white", note: "B", topWidth: 1.3, bottomWidth: 2.4, topPositionX: 0.55, wholePositionX: 0},
        ]
    
        // Transform Node that acts as the parent of all piano keys
        const keyboard = new BABYLON.TransformNode("keyboard");
    
        // Register 1 through 7
        var referencePositionX = -2.4*14;
        for (let register = 1; register <= 7; register++) {
            keyParams.forEach(key => {
                buildKey(scene, keyboard, Object.assign({register: register, referencePositionX: referencePositionX}, key));
            })
            referencePositionX += 2.4*7;
        }
    
        // Register 0
        buildKey(scene, keyboard, {type: "white", note: "A", topWidth: 1.9, bottomWidth: 2.3, topPositionX: -0.20, wholePositionX: -2.4, register: 0, referencePositionX: -2.4*21});
        keyParams.slice(10, 12).forEach(key => {
            buildKey(scene, keyboard, Object.assign({register: 0, referencePositionX: -2.4*21}, key));
        })
    
        // Register 8
        buildKey(scene, keyboard, {type: "white", note: "C", topWidth: 2.3, bottomWidth: 2.3, topPositionX: 0, wholePositionX: -2.4*6, register: 8, referencePositionX: 84});
    
        // Transform node that acts as the parent of all piano components
        const piano = new BABYLON.TransformNode("piano");
        keyboard.parent = piano;
    
        // Import and scale piano frame
        BABYLON.SceneLoader.ImportMesh("frame", "https://raw.githubusercontent.com/MicrosoftDocs/mixed-reality/docs/mixed-reality-docs/mr-dev-docs/develop/javascript/tutorials/babylonjs-webxr-piano/files/", "pianoFrame.babylon", scene, function(meshes) {
            const frame = meshes[0];
            frame.parent = piano;
        });
    
        // Lift the piano keyboard
        keyboard.position.y += 80;
    
        const xrHelper = await scene.createDefaultXRExperienceAsync();
    
        return scene;
    }
    
  5. Sekarang kita harus memiliki piano standup yang terlihat seperti ini: Standup Piano Mesh

Langkah berikutnya