Öğretici: 3B piyano modeli oluşturma

Serinin önceki öğreticisinde, kamera ve ışık içeren bir Babylon.js sahnesi içeren bir web sayfası ayarladık. Bu öğreticide, sahneye bir piyano modeli oluşturacak ve ekleyeceğiz.

Standup Piano Mesh

Bu öğreticide şunların nasıl yapıldığını öğrenirsiniz:

  • Tire oluşturma, konumlandırma ve birleştirme
  • Kutu kafeslerinden piyano klavyesi oluşturma
  • Piyano çerçevesinin 3B modelini içeri aktarma

Başlamadan önce

Serinin önceki öğreticisini tamamladığınızdan ve koda eklemeye devam etmeye hazır olduğunuzdan emin olun.

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

Başlarken

Bu yapıya sahip basit bir piyano klavyesi yaparak başlayalım:

Piyano yazmaç açıklaması

Bu görüntüde, her biri not adıyla etiketlenmiş 7 beyaz ve 5 siyah tuş vardır. Tam 88 tuşlu piyano klavyesi, bu tuş seçiminin 7 tam tekrarını (yazmaç olarak da adlandırılır) ve 4 ekstra tuş içerir. Her yazmaç, önceki yazmaç sıklığının iki katıdır. Örneğin, C5'in perde sıklığı (beşinci yazmaçtaki C notu) C4'ün iki katı, D5'in perde sıklığı D4'ün iki katı, vb. olur.

Görsel olarak, her yazmaç aynı şekilde görünür, bu nedenle bu tuş seçimiyle basit bir piyano klavyesinin nasıl oluşturulacağını araştırmakla başlayabiliriz. Daha sonra kapsamı 88 tuşlu tam piyano klavyesine genişletmenin bir yolunu bulabiliriz.

Basit bir piyano klavyesi oluşturma

Not

Piyano klavyelerinin önceden hazırlanmış 3B modellerini çevrimiçi kaynaklardan bulup web sayfamıza aktarmak mümkün olsa da, en fazla özelleştirilebilirliğe izin vermek ve Babylon.js aracılığıyla 3B modellerin nasıl oluşturulabileceğini göstermek için bu öğreticide klavyeyi sıfırdan oluşturacağız.

  1. Klavyeyi oluşturmak için herhangi bir tire oluşturmaya başlamadan önce, her siyah tuşun çevresindeki iki beyaz tuşlarına mükemmel bir şekilde hizalanmadığını ve her tuşun aynı genişliğe sahip olmadığını fark edin. Bu, her anahtar için ağı ayrı ayrı oluşturmamız ve konumlandırmamız gerektiği anlamına gelir.

    Siyah Tuş Hizalama

  2. Beyaz tuşlar için, her beyaz anahtarın iki bölümden oluştuğunu gözlemleyebiliriz: (1) siyah tuşların altındaki alt kısım ve (2) siyah tuşların yanındaki üst kısım. İki parçanın farklı boyutları vardır, ancak tam bir beyaz anahtar girebilmek için birlikte yığılır.

    Beyaz Anahtar Şekli

  3. C notu için tek bir beyaz anahtar oluşturma kodu aşağıdadır (bunu henüzscene.js ekleme konusunda endişelenmeyin):

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

    Burada biri alt kısım, diğeri de beyaz anahtarın üst kısmı için iki Kutu tire oluşturduk. Daha sonra üst kısmın konumunu değiştirerek alt kısmın üstüne yığacağız ve komşu siyah tuşa (C#) yer bırakmak üzere sola doğru hareket ettireceğiz.

    Son olarak, bu iki bölüm MergeMeshes işlevi kullanılarak birleştirilerek tam bir beyaz anahtar haline geldi. Bu kodun üreteceği sonuçta elde edilen ağ budur:

    Beyaz Anahtar C

  4. Siyah anahtar oluşturmak daha kolaydır. Tüm siyah tuşlar bir kutu şeklinde olduğundan, siyah renkli StandardMaterial ile bir kutu ağı oluşturarak siyah bir anahtar oluşturabiliriz.

    Not

    Varsayılan mesh rengi beyaza benzeyen açık gri olduğundan, bu öğreticide beyaz tuşlara beyaz renk malzemesi ekleme adımları yer almaz. Ancak, beyaz tuşlara gerçek, parlak beyaz bir renk istiyorsanız malzemeyi kendiniz ekleyebilirsiniz.

    Siyah anahtar C# oluşturmak için kod aşağıdadır (bunu dascene.js ekleme konusunda endişelenmeyin):

    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;
    

    Bu kod tarafından üretilen siyah anahtar (önceki beyaz anahtarla birlikte) şöyle görünür:

    Siyah Anahtar C#

  5. Gördüğünüz gibi, her anahtarın oluşturulması birçok benzer koda neden olabilir çünkü bunların boyutlarını ve konumlarını belirtmemiz gerekir. Bir sonraki bölümde oluşturma işlemini daha verimli hale getirmeye çalışalım.

Basit bir piyano klavyeyi verimli bir şekilde oluşturun

  1. Her beyaz tuş birbirinden biraz farklı bir şekle sahip olsa da, bunların tümü üst kısım ve alt kısım birleştirilerek oluşturulabilir. Şimdi herhangi bir beyaz anahtar oluşturmak ve konumlandırmak için genel bir işlev uygulayalım.

    aşağıdaki işlevi işlevinin dışına createScene()scene.jsekleyin:

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

    Bu kod bloğunda, ise props.type"white"beyaz anahtar oluşturan ve döndüren adlı buildKey()bir işlev oluşturduk. parametresindeki propsanahtarın türünü tanımlayarak, if-deyimini kullanarak dallanarak aynı işlevde hem siyah hem de beyaz anahtarlar oluşturabiliriz.

    parametreleri buildKey() şunlardır:

    • sahne: anahtarın içinde olduğu sahne
    • parent: ağın üst öğesi (bu, tüm anahtarları tek bir üst öğe olarak gruplandırmamızı sağlar)
    • props: oluşturulacak anahtarın özellikleri

    props Beyaz anahtar için aşağıdaki öğeleri içerir:

    • type: "white"
    • name: anahtarın temsil ettiği notun adı
    • topWidth: üst kısmın genişliği
    • bottomWidth: alt kısmın genişliği
    • topPositionX: Üst kısmın alt bölüme göre x konumu
    • wholePositionX: Yazmaç bitiş noktasına göre tüm anahtarın x konumu (B anahtarının sağ kenarı).
    • register: anahtarın ait olduğunu kaydedin (0 ile 8 arasında bir sayı)
    • referencePositionX: Yazmaç uç noktasının x koordinatı (başvuru noktası olarak kullanılır).

    ve değerlerini ayırarakwholePositionX, herhangi bir yazmaç içinde belirli bir anahtar türü (örn. C) oluşturmak için gereken parametreleri başlatabiliyor props ve bu anahtarı belirli bir yazmaçta oluştururken (örn. C4, C5) üzerine ve propsreferencePositionX ekliyoruzregister.referencePositionX

  2. Benzer şekilde, siyah anahtar oluşturmak için genel bir işlev de yazabiliriz. Şimdi işlevi bu mantığı içerecek şekilde genişletelim 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 Siyah anahtar için aşağıdaki öğeleri içerir:

    • type: "black"
    • name: anahtarın temsil ettiği notun adı
    • wholePositionX: Yazmaç bitiş noktasına göre tüm anahtarın x konumu (B anahtarının sağ kenarı)
    • register: anahtarın ait olduğunu kaydedin (0 ile 8 arasında bir sayı)
    • referencePositionX: Yazmaç uç noktasının x koordinatı (başvuru noktası olarak kullanılır).

    props Siyah anahtar oluşturma işlemi çok daha basittir çünkü siyah anahtar oluşturmak yalnızca bir kutu oluşturmayı içerir ve her siyah anahtarın genişliği ve z konumu aynıdır.

  3. Artık anahtarları oluşturmanın daha verimli bir yoluna sahip olduğumuza göre, yazmaçtaki bir nota karşılık gelen her tuş için öğesini depolayan props bir dizi başlatalım ve ardından 4. yazmacında basit bir klavye oluşturmak için her biriyle işlevini çağıralım buildKey() .

    Ayrıca, tüm piyano tuşlarının ebeveyni olarak hareket etmek için adlı keyboard bir TransformNode oluşturacağız. Üst öğeye uygulanan herhangi bir konum veya ölçeklendirme değişikliği de çocuklara uygulanacağı için anahtarları bu şekilde gruplandırmak, anahtarları bir bütün olarak ölçeklendirmemize veya taşımamıza olanak sağlar.

    İşleve aşağıdaki kod satırlarını createScene() ekleyin:

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

    Muhtemelen fark ettiğiniz gibi, bu kod bloğunda tüm anahtarları alanın kaynağına göre yerleştiriyoruz.

  4. scene.jsşu ana kadar içerdiği kod aşağıdadır:

    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. Sonuçta elde edilen klavye şöyle görünür:

    Tek Yazmaçlı Piyano Klavyesi

88 tuşlu piyanoya genişletiliyor

Bu bölümde, tam 88 tuşlu bir piyano klavyesi oluşturmak için tuş oluşturma işlevlerinin kullanımını genişletelim.

  1. Daha önce belirtildiği gibi, tam, 88 tuşlu bir piyano klavyesi 7 tekrarlı yazmaç ve 4 nota daha içerir. Bu ek notların 3'ünün yazmaç 0'da (klavyenin sol ucu) ve 1'i yazmaç 8'de (klavyenin sağ ucu) bulunur.

    88 tuşlu piyano düzeni

  2. İlk olarak daha önce yazdığımız döngüye ek bir döngü ekleyerek 7 tam yinelemeyi oluşturmaya çalışacağız. işlevinin önceki döngüsünü buildKey() aşağıdaki kodla değiştirin:

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

    Bu döngüde 1 ile 7 arasında yazmaç anahtarlarını derleyeceğiz ve bir sonraki yazmaçta her geçişte başvuru konumunu artıracağız.

  3. Şimdi diğer anahtarları oluşturalım. İşleve aşağıdaki kod parçacığını createScene() ekleyin:

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

    Piyano klavyesinin en sol ve en sağdaki tuşunun içinde tanımlanan props boyutlarına uymadığını unutmayın (kenarda keyParams siyah bir tuşun yanında olmadıkları için), bu nedenle her biri için özel şekillerini belirtmek üzere yeni props bir nesne tanımlamamız gerekir.

  4. Oluşturulan klavye, değişiklikler yapıldıktan sonra aşağıdaki gibi görünmelidir:

    Full Piano Keyboard Mesh

Piyano çerçevesi ekleme

  1. Alanda yalnızca bir klavyenin dolaştığınız sahne biraz garip görünüyor. Şimdi bir standup piyano görünümü oluşturmak için klavyenin çevresine bir piyano çerçevesi ekleyelim.

  2. Anahtarları nasıl oluşturduğumuza benzer şekilde, bir grup kutu kısa çizgisini konumlandırıp birleştirerek çerçeveyi de oluşturabiliriz.

    Ancak, bu sınamayı kendi başınıza denemeniz ve BABYLON kullanmanız için bırakacağız . SahneYükleyici.ImportMesh standup piyano çerçevesi önceden yapılmış bir örgü içeri aktarmak için. Bu kod parçasını sonuna createScene()ekleyin:

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

    Yine klavyeyi ve çerçeveyi bir bütün olarak gruplandırmak için adlı piano bir üst TransformNode öğe oluşturduğumuza dikkat edin. Bu, gerekirse piyanonun tamamını taşımayı veya ölçeklendirmeyi çok daha kolay hale getirir.

  3. Çerçeve içeri aktarıldıktan sonra, klavyenin çerçevenin en altında olduğuna dikkat edin (tuşların y koordinatları varsayılan olarak 0'dır). Şimdi klavyeyi, standup piyano çerçevesine sığacak şekilde kaldıralım:

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

    Tüm piyano tuşlarının ebeveyni olduğundan keyboard , sadece y konumunu değiştirerek tüm piyano tuşlarını keyboardkaldırabiliriz.

  4. scene.js son kodu şu şekilde görünmelidir:

    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. Şimdi şuna benzeyen bir standup piyanomuz olmalı: Standup Piano Mesh

Sonraki adımlar