Sdílet prostřednictvím


Kurz: Vytvoření 3D modelu klavíru

V předchozím kurzu v sérii jsme nastavili webovou stránku obsahující Babylon.js scénu s kamerou a kontrolkou. V tomto kurzu budeme vytvářet a přidávat do scény model klavíru.

Standup Piano Mesh

V tomto kurzu se naučíte, jak:

  • Vytváření, umístění a slučování sítí
  • Vytvoření klávesy pro piano z krabicových sítí
  • Import 3D modelu klavírního rámu

Než začnete

Ujistěte se, že jste prošli předchozím kurzem v sérii a jste připraveni pokračovat v přidávání do kódu.

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

Začínáme

Začněme tím, že vytvoříte jednoduchou klávesovou klávesnici, která má tuto strukturu:

Popis klavírní pokladny

Na tomto obrázku je 7 bílých kláves a 5 černých kláves, z nichž každá je označená názvem poznámky. Úplná klávesová klávesnice s 88 klávesami obsahuje 7 úplných opakování tohoto výběru kláves (označovaných také jako registr) a 4 další klávesy. Každý registr má dvojnásobnou frekvenci oproti předchozímu rejstříku. Například frekvence rozteče C5 (což znamená poznámku C v pátém rejstříku) je dvojnásobná od C4, frekvence rozteče D5 je dvojnásobná od D4 a tak dále.

Vizuálně vypadá každý registr úplně stejně jako jiný, takže můžeme začít zkoumáním toho, jak vytvořit jednoduchou klávesovou klávesnici s tímto výběrem kláves. Později najdeme způsob, jak rozšířit rozsah na 88 kláves s plnými klávesovými klávesy.

Vytvoření jednoduché klávesy pro piano

Poznámka

I když je možné najít předem vytvořené 3D modely klavírních klávesnic z online zdrojů a importovat je na naši webovou stránku, v tomto kurzu sestavíme klávesnici od začátku, abychom umožnili maximální přizpůsobení a ukázali, jak lze 3D modely vytvářet prostřednictvím Babylon.js.

  1. Než začneme vytvářet nějaké sítě pro sestavení klávesnice, všimněte si, že každá černá klávesa není dokonale zarovnaná uprostřed dvou bílých kláves kolem a ne každá klávesa má stejnou šířku. To znamená, že musíme vytvořit a umístit síť pro každý klíč zvlášť.

    Zarovnání černé klávesy

  2. U bílých kláves můžeme zjistit, že každá bílá klávesa se skládá ze dvou částí: (1) dolní část pod černými klávesami a (2) horní část vedle černých kláves. Tyto dvě části mají různé rozměry, ale jsou poskládány dohromady, aby kréta plně bílý klíč.

    Obrazec s bílou klávesou

  3. Tady je kód pro vytvoření jediného bílého klíče pro poznámku C (zatím si nedělejte starosti s přidáním do 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";
    

    Zde jsme vytvořili dvě krabicové sítě, jednu pro spodní část a druhou pro horní část bílé klávesy. Potom upravíme pozici horní části tak, aby se naskládaly na spodní část, a přesuneme ji směrem doleva, aby zůstal prostor pro sousední černý klíč (C#).

    Nakonec byly tyto dvě části sloučeny pomocí funkce MergeMeshes , aby se stala jednou úplnou bílou klávesou. Toto je výsledná síť, kterou tento kód vytvoří:

    Bílá klávesa C

  4. Vytvoření černé klávesy je jednodušší. Vzhledem k tomu, že všechny černé klávesy mají tvar krabice, můžeme vytvořit černý klíč pouhým vytvořením krabicové mřížky s černou barvou StandardMaterial.

    Poznámka

    Vzhledem k tomu, že výchozí barva sítě je světle šedá, která se podobá bílé, tento kurz neobsahuje kroky pro přidání bílého barevného materiálu k bílým klávesám. Pokud byste ale chtěli pravou, jasně bílou barvu na bílých klávesách, můžete si ho přidat sami.

    Tady je kód pro vytvoření černého klíče V jazyce C# (nebojte se ho přidat do 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;
    

    Černý klíč vytvořený tímto kódem (spolu s předchozí bílou klávesou) by vypadal takto:

    Černá klávesa C#

  5. Jak vidíte, vytvoření každého klíče může vést k mnoha podobným kódům, protože musíme zadat každou z jejich dimenzí a umístění. V další části se pokusíme proces vytváření zefektivnit.

Efektivní vytvoření jednoduché klávesnice pro piano

  1. I když má každá bílá klávesa trochu jiný tvar, všechny je možné vytvořit kombinací horní a dolní části. Pojďme implementovat obecnou funkci, která vytvoří a umístí libovolnou bílou klávesu.

    Přidejte následující funkci do scene.jsmimo funkci createScene() :

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

    V tomto bloku kódu jsme vytvořili funkci s názvem buildKey(), která sestaví a vrátí bílý klíč, pokud props.type je "white". Identifikací typu klíče v parametru propsmůžeme vytvořit černé i bílé klíče ve stejné funkci pomocí příkazu if-.

    Parametry jsou buildKey() :

    • scene: scéna, ve které je klíč
    • nadřazený: nadřazený objekt sítě (to nám umožňuje seskupit všechny klíče do jednoho nadřazeného objektu)
    • rekvizit: vlastnosti klíče, který bude sestaven

    Bílý props klíč bude obsahovat následující položky:

    • type: "white"
    • name: název poznámky, kterou klíč představuje.
    • topWidth: šířka horní části
    • bottomWidth: šířka dolní části
    • topPositionX: x-position of the top part vzhledem k dolní části
    • wholePositionX: x pozice celé klávesy vzhledem ke koncovému bodu registru (pravý okraj klíče B).
    • register: zaregistrujte, ke kterému klíč patří (číslo mezi 0 a 8)
    • referencePositionX: souřadnice x koncového bodu registru (používá se jako referenční bod).

    wholePositionX Oddělením a referencePositionXjsme schopni inicializovat props parametry potřebné k vytvoření konkrétního typu klíče (např. C) v rámci libovolného registru a potom přidat register do a referencePositionXprops při vytváření tohoto klíče v konkrétním registru (např. C4, C5).

  2. Podobně můžeme také napsat obecnou funkci pro vytvoření černé klávesy. Pojďme funkci rozšířit, buildKey() aby zahrnovala tuto logiku:

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

    Černá klávesa props pro obsahuje následující položky:

    • type: "black"
    • name: název poznámky, kterou klíč představuje.
    • wholePositionX: x-position celého klíče vzhledem ke koncovému bodu registru (pravý okraj klíče B)
    • register: zaregistrujte, ke kterému klíč patří (číslo mezi 0 a 8)
    • referencePositionX: souřadnice x koncového bodu registru (používá se jako referenční bod).

    Vytvoření props černé klávesy je mnohem jednodušší, protože vytvoření černé klávesy zahrnuje pouze vytvoření rámečku a šířka každého černého klíče a pozice z jsou stejné.

  3. Teď, když máme efektivnější způsob vytváření kláves, inicializujeme pole, které ukládá props pro každou klávesu, která odpovídá poznámce v registru, a pak zavoláme buildKey() funkci s každou z nich, abychom vytvořili jednoduchou klávesnici ve 4. registru.

    Vytvoříme také TransformNode s názvem keyboard , který bude fungovat jako nadřazený prvek všech kláves pro klavír. Vzhledem k tomu, že jakákoli změna pozice nebo škálování použitá u nadřazeného objektu by se použila i na podřízené položky, umožní nám seskupit klíče tímto způsobem a pak je škálovat nebo přesouvat jako celek.

    Do funkce připojte následující řádky createScene() kódu:

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

    Jak jste si asi všimli, v tomto bloku kódu umisťujeme všechny klíče vzhledem k počátku mezery.

  4. Tady je kód, který scene.js zatím obsahuje:

    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. Výsledná klávesnice by vypadala takto:

    Klávesová klávesnice s jedním registrem

Rozšíření na klavír s 88 klávesy

V této části rozšíříme použití funkcí pro vytváření kláves a vygenerování plné 88 klávesové klávesnice.

  1. Jak už bylo zmíněno dříve, plná klávesová klávesnice s 88 klávesami obsahuje 7 opakovaných registrů a 4 další poznámky. 3 z těchto dodatečných poznámek jsou v registru 0 (levý konec klávesnice) a 1 je v registru 8 (pravý konec klávesnice).

    Rozložení klavíru s 88 klávesy

  2. Nejprve budeme pracovat na sestavení 7 úplných opakování přidáním další smyčky kolem smyčky, kterou jsme napsali dříve. Předchozí smyčku pro buildKey() funkci nahraďte následujícím kódem:

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

    V této smyčce sestavíme klíče pro registr 1 až 7 a pokaždé, když přejdeme k dalšímu registru, zvýšíme referenční pozici.

  3. V dalším kroku vytvoříme zbývající klíče. Přidejte do createScene() funkce následující fragment kódu:

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

    Všimněte si, že klávesa nejvíce vlevo a pravá klávesa klavírní klávesnice nezapadají do rozměrů rekvizit definovaných v keyParams (protože nejsou vedle černé klávesy na okraji), takže musíme definovat nový props objekt pro každou z nich, abychom mohli určit jejich speciální tvar.

  4. Vytvořená klávesnice by měla po provedení změn vypadat takto:

    Úplná klávesová síť

Přidání rámu pro klavír

  1. Scéna vypadá trochu divně, v prostoru je jen klávesnice. Pojďme přidat rámeček klavíru kolem klávesnice, abychom vytvořili vzhled standup piano.

  2. Podobně jako jsme vytvořili klávesy, můžeme také vytvořit rámeček umístěním a kombinováním skupiny krabicových sítí.

    Tuto výzvu však necháme pro vás, abyste si to vyzkoušeli sami a použili BABYLON. SceneLoader.ImportMesh k importu předem vytvořené sítě ze standup piano frame. Připojte tuto část kódu k 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;
    });
    

    Všimněte si, že opět vytváříme nadřazený TransformNode objekt s názvem piano , který seskupí klávesnici a rámeček dohromady jako celek. To výrazně usnadní přesunutí nebo škálování celého klavíru, pokud bychom to někdy potřebovali.

  3. Po importu rámce si všimněte, že klávesnice leží v dolní části rámce (protože souřadnice kláves y jsou ve výchozím nastavení na hodnotě 0). Zvedněme klávesnici, aby se vešla do standup rámu klavíru:

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

    Vzhledem k tomu keyboard , že je nadřazená všechny klavírní klávesy, můžeme všechny klavírní klávesy zvednout jednoduše tak, že změníme pozici keyboardy pro .

  4. Konečný kód scene.js by měl vypadat takto:

    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. Teď bychom měli mít standup piano, které vypadá takto: Standup Piano Mesh

Další kroky