Condividi tramite


Esercitazione: Creare un modello di pianoforte 3D

Nell'esercitazione precedente della serie è stata configurata una pagina Web contenente una scena Babylon.js con una fotocamera e una luce. In questa esercitazione verrà creato e aggiunto un modello di pianoforte nella scena.

Standup Piano Mesh

In questa esercitazione verranno illustrate le procedure per:

  • Creare, posizionare e unire mesh
  • Creare una tastiera per pianoforte da mesh box
  • Importare un modello 3D di una cornice piano

Prima di iniziare

Assicurarsi di aver eseguito l'esercitazione precedente della serie e di continuare ad aggiungere il codice.

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;
}

Introduzione

Iniziamo creando una semplice tastiera per pianoforte con questa struttura:

Descrizione del registro piano

In questa immagine sono presenti 7 chiavi bianche e 5 chiavi nere, ognuna con il nome della nota. Una tastiera completa a 88 tasti contiene 7 ripetizioni complete di questa selezione di tasti (detto anche registro) e 4 tasti aggiuntivi. Ogni registro ha il doppio della frequenza del registro precedente. Ad esempio, la frequenza di inclinazione di C5 (il che significa che la nota C nel quinto registro) è doppia del C4, la frequenza di inclinazione di D5 è doppia di D4 e così via.

Visivamente, ogni registro sembra esattamente uguale a un altro, quindi possiamo iniziare con l'analisi su come creare una semplice tastiera per pianoforte con questa selezione di tasti. Successivamente, è possibile trovare un modo per espandere l'ambito a una tastiera completa a 88 tasti.

Creare una semplice tastiera per pianoforte

Nota

Anche se è possibile trovare modelli 3D predefiniti di tastiere da origini online e importarli nella nostra pagina Web, creeremo la tastiera da zero in questa esercitazione per consentire la massima personalizzazione e mostrare come i modelli 3D possono essere creati tramite Babylon.js.

  1. Prima di iniziare a creare mesh per la compilazione della tastiera, si noti che ogni tasto nero non è perfettamente allineato al centro dei due tasti bianchi intorno, e non tutti i tasti hanno la stessa larghezza. Ciò significa che è necessario creare e posizionare la mesh per ogni chiave singolarmente.

    Allineamento chiave nera

  2. Per i tasti bianchi, è possibile osservare che ogni chiave bianca è composta da due parti: (1) la parte inferiore sotto i tasti neri e (2) la parte superiore accanto ai tasti neri. Le due parti hanno dimensioni diverse, ma sono impilate insieme a creta una chiave bianca completa.

    Forma chiave bianca

  3. Ecco il codice per la creazione di una singola chiave bianca per la nota C (non preoccuparti ancora di aggiungerlo in scene.js ):

    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";
    

    Qui abbiamo creato due mesh box , una per la parte inferiore e una per la parte superiore della chiave bianca. Si modifica quindi la posizione della parte superiore per impilarla sopra la parte inferiore e spostarla verso sinistra per lasciare spazio per la chiave nera adiacente (C#).

    Infine, queste due parti sono state unite usando la funzione MergeMeshes per diventare una chiave bianca completa. Questa è la mesh risultante che questo codice produrrebbe:

    Chiave bianca C

  4. La creazione di una chiave nera è più semplice. Poiché tutti i tasti neri sono della forma di una scatola, è possibile creare una chiave nera solo creando una mesh di box con un StandardMaterial di colore nero.

    Nota

    Poiché il colore di mesh predefinito è un grigio chiaro simile al bianco, questa esercitazione non include i passaggi per aggiungere un materiale di colore bianco alle chiavi bianche. Tuttavia, è possibile aggiungere manualmente il materiale se si vuole un vero e brillante colore bianco sui tasti bianchi.

    Ecco il codice per creare la chiave nera C# (non preoccuparti di aggiungerlo a scene.js ):

    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;
    

    La chiave nera prodotta da questo codice (insieme alla chiave bianca precedente) sarà simile alla seguente:

    Tasto nero C#

  5. Come si può notare, la creazione di ogni chiave può comportare un numero elevato di codice simile perché è necessario specificare ognuna delle dimensioni e della posizione. Provare a rendere il processo di creazione più efficiente nella sezione successiva.

Creare una tastiera di piano semplice in modo efficiente

  1. Mentre ogni chiave bianca ha una forma leggermente diversa l'una dall'altra, tutte possono essere create combinando una parte superiore e una parte inferiore. Verrà ora implementata una funzione generica per creare e posizionare qualsiasi chiave bianca.

    Aggiungere la funzione seguente a scene.js, all'esterno della createScene() funzione :

    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;
        }
    }
    

    In questo blocco di codice è stata creata una funzione denominata buildKey(), che compila e restituisce una chiave bianca se props.type è "white". Identificando il tipo di chiave nel parametro props, è possibile creare sia chiavi nere che chiavi bianche nella stessa funzione diramando usando un'istruzione if.

    I parametri di buildKey() sono:

    • scena: scena in cui si trova la chiave
    • padre: elemento padre della mesh (in questo modo è possibile raggruppare tutte le chiavi in un singolo elemento padre)
    • props: proprietà della chiave che verrà compilata

    L'oggetto props per una chiave bianca conterrà gli elementi seguenti:

    • type: "white"
    • name: nome della nota che rappresenta la chiave
    • topWidth: larghezza della parte superiore
    • bottomWidth: larghezza della parte inferiore
    • topPositionX: posizione x della parte superiore rispetto alla parte inferiore
    • wholePositionX: posizione x dell'intera chiave rispetto al punto finale del registro (il bordo destro della chiave B).
    • register: registrare che la chiave appartiene (un numero compreso tra 0 e 8)
    • referencePositionX: coordinata x del punto finale del registro (utilizzata come punto di riferimento).

    Separando wholePositionX e referencePositionX, è possibile inizializzare i props parametri necessari per creare un tipo specifico di chiave (ad esempio C) all'interno di qualsiasi registro e quindi aggiungere register e referencePositionX all'oggetto durante la props creazione di tale chiave in un registro specifico (ad esempio C4, C5).

  2. Analogamente, è anche possibile scrivere una funzione generica per creare una chiave nera. Espandere la buildKey() funzione per includere tale logica:

    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;
        }
    }
    

    L'oggetto props per una chiave nera contiene gli elementi seguenti:

    • type: "black"
    • name: nome della nota che rappresenta la chiave
    • wholePositionX: posizione x dell'intera chiave rispetto al punto finale del registro (il bordo destro della chiave B)
    • register: registrare che la chiave appartiene (un numero compreso tra 0 e 8)
    • referencePositionX: coordinata x del punto finale del registro (utilizzata come punto di riferimento).

    Per props creare una chiave nera è molto più semplice perché la creazione di una chiave nera comporta solo la creazione di una casella e la larghezza e la posizione z di ogni tasto nero sono uguali.

  3. Ora che è disponibile un modo più efficiente per creare i tasti, inizializzare una matrice che archivia per props ogni tasto che corrisponde a una nota in un registro e quindi chiamare la buildKey() funzione con ognuna di esse per creare una tastiera semplice nel quarto registro.

    Verrà anche creato un oggetto TransformNode denominato keyboard per fungere da padre di tutte le chiavi del piano. Poiché qualsiasi modifica di posizione o ridimensionamento applicata all'elemento padre verrà applicata anche agli elementi figlio, il raggruppamento delle chiavi in questo modo consentirà di ridimensionarli o spostarli nel suo complesso.

    Aggiungere le righe di codice seguenti nella createScene() funzione :

    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));
    })
    

    Come probabilmente si è notato, in questo blocco di codice vengono collocate tutte le chiavi relative all'origine dello spazio.

  4. Ecco il codice che scene.js contiene finora:

    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. Questo è l'aspetto della tastiera risultante:

    Tastiera piano con un unico registro

Espansione in un pianoforte a 88 tasti

In questa sezione si espanderà l'utilizzo delle funzioni di creazione di tasti per generare una tastiera a 88 tasti completa.

  1. Come accennato in precedenza, una tastiera a 88 tasti completa contiene 7 registri ripetuti e 4 altre note. 3 di queste note aggiuntive sono nel registro 0 (estremità sinistra della tastiera) e 1 è nel registro 8 (estremità destra della tastiera).

    Layout piano a 88 tasti

  2. Prima di tutto lavoreremo sulla creazione delle 7 ripetizioni complete aggiungendo un ciclo aggiuntivo intorno al ciclo scritto in precedenza. Sostituire il ciclo precedente per la buildKey() funzione con il codice seguente:

    // 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;
    }
    

    In questo ciclo vengono compilate le chiavi per la registrazione da 1 a 7 e si incrementa la posizione di riferimento ogni volta che si passa al registro successivo.

  3. Creare quindi il resto delle chiavi. Aggiungere il frammento di codice seguente alla createScene() funzione:

    // 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});
    

    Si noti che il tasto più a sinistra e il tasto più a destra della tastiera del pianoforte non rientrano nelle dimensioni delle proprietà definite in keyParams (perché non sono accanto a un tasto nero al bordo), quindi è necessario definire un nuovo props oggetto per ognuno di essi per specificare la forma speciale.

  4. La tastiera prodotta dovrebbe essere simile alla seguente dopo aver apportato le modifiche:

    Mesh della tastiera a piano completo

Aggiunta di una cornice per pianoforte

  1. La scena sembra un po 'strano con solo una tastiera mobile nello spazio. Aggiungiamo una cornice di pianoforte intorno alla tastiera per creare l'aspetto di un pianoforte standup.

  2. Analogamente al modo in cui sono state create le chiavi, è anche possibile creare la cornice posizionando e combinando un gruppo di mesh di caselle.

    Tuttavia, lasceremo quella sfida per voi di provare a usare BABilonia e di usarla. SceneLoader.ImportMesh per importare una mesh pre-fatta di una cornice di piano standup. Aggiungere questo pezzo di codice a 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;
    });
    

    Si noti che, di nuovo, la creazione di un elemento padre TransformNode denominato piano per raggruppare la tastiera e la cornice insieme come intero. Ciò renderà più semplice lo spostamento o la scalabilità dell'intero pianoforte se mai dobbiamo farlo.

  3. Dopo l'importazione della cornice, si noti che la tastiera si trova nella parte inferiore del frame (poiché le coordinate y dei tasti sono a 0 per impostazione predefinita). È possibile sollevare la tastiera in modo che si adatta alla cornice di piano standup:

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

    Poiché keyboard è l'elemento padre di tutti i tasti di piano, è possibile sollevare tutti i tasti del piano modificando semplicemente la posizione y di keyboard.

  4. Il codice finale di scene.js dovrebbe essere simile al seguente:

    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. Ora dovremmo avere un pianoforte standup simile al seguente: Standup Piano Mesh

Passaggi successivi