Dropdown to appear on hover of mesh

Sounds nice :smile:

It means you have something wrong somewhere in the ImportMesh callback. Unfortunately I cannot help you without the complete code. Do some debugging, try removing some parts of the code and check if it works without. If it does, than it somewhere inside that part of code, if it still doesn’t then it’s somewhere else. You need to narrow down where the issue is.

1 Like

This is an example of the whole code. I’ve been removing parts to see if it works but no luck so far. So I don’t know if you have any ideas @nogalo?

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>

<style>
    html, body {
        overflow: hidden;
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
    }

    #renderCanvas {
        width: 100%;
        height: 100%;
        touch-action: none;
    }
</style>

<script src="https://cdn.jsdelivr.net/npm/babylonjs@4.0.3/babylon.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babylonjs-loaders@4.0.3/babylonjs.loaders.min.js"></script>
<!-- For pointer events on tab/mob -->
<script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
</head>

<body>

<canvas id="renderCanvas" touch-action="none"></canvas>

<script>
    var canvas = document.getElementById("renderCanvas"); // Get the canvas element 
    var engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true }); // Generate the BABYLON 3D engine

    /******* Add the create scene function ******/
    var createScene = function () {         

        // Create the scene space
        var scene = new BABYLON.Scene(engine);
        scene.ambientColor = new BABYLON.Color3(.1, .1, .1);

        /********** ARC ROTATE CAMERA **************************/
        // Creates, angles, distances and targets the camera
        var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 10, new BABYLON.Vector3(0, 0, 0), scene);
        // This positions the camera
        camera.setPosition(new BABYLON.Vector3(-2, 2, -7));
        // This attaches the camera to the canvas
        camera.attachControl(canvas, true);
        camera.lowerRadiusLimit = camera.upperRadiusLimit = camera.radius;
        /*******************************************************/
        
        // Add Light
        var light = new BABYLON.HemisphericLight("HemiLight", new BABYLON.Vector3(0, 1, 0), scene);
        light.intensity = .8;
        light.specular = new BABYLON.Color3(0,0,0);
    
    // Import meshes
    BABYLON.SceneLoader.ImportMesh(["Cube", "Cylinder", "Cylinder.001", "Cylinder.002", "Cylinder.003"], 
    "https://raw.githubusercontent.com/ryannewelluk/3dcar/master/", "basic-redcar.glb", scene, function (meshes) {
        
    var car = meshes[0];
        car.scaling = new BABYLON.Vector3(0.25, 0.25, 0.25);
    var block = meshes[1];    
    var frontWheel = meshes[2];
        frontWheel.position.x = 5; 
    var frontWheel2 = meshes[3];
             
        
    // GUI
    var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
        advancedTexture.useInvalidateRectOptimization = false;
                    
    let actionManager = new BABYLON.ActionManager(scene);
        block.actionManager = actionManager; 
        frontWheel.actionManager = actionManager;
        frontWheel2.actionManager = actionManager; 
    
    //creat stack panel add it as root element to advancedTexture and scale it to 0
    let panel = new BABYLON.GUI.StackPanel(); 
        advancedTexture.addControl(panel);    
        panel.transformCenterY = 0;       
        panel.scaleX = 0;
        panel.scaleY = 0;

    //now add options (rect1, rect2) -> those are now children of the panel element, you can create these as many as you want and add it to panel
    let rect1 =  new BABYLON.GUI.Rectangle();
        panel.addControl(rect1)
    let text1 = new BABYLON.GUI.TextBlock();
        rect1.addControl(text1);    

    let rect2 = new BABYLON.GUI.Button.CreateSimpleButton("but1", "Option1");  
        panel.addControl(rect2)  
    let rect3 = new BABYLON.GUI.Button.CreateSimpleButton("but2", "Option2");  
        panel.addControl(rect3) 
    let rect4 = new BABYLON.GUI.Button.CreateSimpleButton("but3", "Option3");  
        panel.addControl(rect4) 
    let rect5 = new BABYLON.GUI.Button.CreateSimpleButton("but4", "Option4");  
        panel.addControl(rect5)  
                     

        //to avoid writing same code for every option in the stackPanel (because they use basically same properties, we can do something like this
       //itarate through every child of panel element and set properties accordingly
        for (let i = 0; i < panel.children.length; i++) {
            let element = panel.children[i];                
            element.width = "160px";
            element.height ="30px";
            element.thickness = 0.2;             
            element.background = "grey";
            element.alpha = 0.9;
            element.fontSize = "16px";
            element.color = "white";       
        }        

        text1.color = "black";
        text1.fontSize = "24px"

    let scaleXAnimation = new BABYLON.Animation("myAnimation", "scaleX", 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
    let scaleYAnimation = new BABYLON.Animation("myAnimation", "scaleY", 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);

    var keys = [];
        keys.push({
                    frame: 0,
                    value: 0
                });
                keys.push({
                frame: 10,
                value: 1
                
                });

        scaleXAnimation.setKeys(keys);
        scaleYAnimation.setKeys(keys);

    //panel is root element so need to switch this lines from rect1 to panel
    panel.animations = [];
    panel.animations.push(scaleXAnimation);
    panel.animations.push(scaleYAnimation);
    
    //create boolean switcher that controls the state of the gui element, is it shown or not, it's important to know if we are already in the menu or not
    let isOpen = false;
    //variable that stores current mesh that we clicked on
    let currentMesh;

     // red mat
    let redMat = scene.getMaterialByName("Material.004");
    // blue mat
    let blueMat = new BABYLON.StandardMaterial("blueMat", scene);
        blueMat.diffuseColor = new BABYLON.Color3.FromHexString("#7fcdff");
    // orange mat
    let orangeMat = new BABYLON.StandardMaterial("orangeMat", scene);
        orangeMat.diffuseColor = new BABYLON.Color3.FromHexString("#FF4500");     
    // purple mat
    let purpMat = new BABYLON.StandardMaterial("purpMat", scene);
        purpMat.diffuseColor = new BABYLON.Color3.FromHexString("#800080");

    actionManager.registerAction(
        new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnLeftPickTrigger, function(ev){
            //if gui IS NOT open -> open it and enable selecting an option
            if (!isOpen) {
                    //panel is root element so need to switch this line from rect1 to panel
                    panel.linkWithMesh(ev.meshUnderPointer);
                    text1.text = ev.meshUnderPointer.name;
                     //as before, show the gui element -> panel
                    scene.beginAnimation(panel, 0, 10, false)  
                    //take the current mesh so you can apply functions to that exact mesh
                    currentMesh = ev.meshUnderPointer;   
                                                                                                           
                }

                //when options from the panel are clicked do something
                rect2.onPointerClickObservable.add(function() {
                    //example -> when first option is clicked call buttonOne function;
                    buttonOne(currentMesh, redMat);
                });

                rect3.onPointerClickObservable.add(function() {
                    //example -> when first option is clicked call buttonTwo function;
                    buttonTwo(currentMesh, blueMat);
                });

                rect4.onPointerClickObservable.add(function() {
                    //example -> when first option is clicked call buttonThree function;
                    buttonThree(currentMesh, orangeMat);
                });

                rect5.onPointerClickObservable.add(function() {
                    //example -> when first option is clicked call buttonFour function;
                    buttonFour(currentMesh, purpMat);
                });

                //set switcher to true 
                isOpen = true;   
            }));        
    
    actionManager.registerAction(
        new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOutTrigger, function(ev) {
                //if gui IS open -> close it 
                if (isOpen) {                     
                    //close the panel                         
                    scene.beginAnimation(panel, 10, 0, false);  
                     text1.text = "";                                         
                }
                //set switcher to false                    
                isOpen = false;
                
    }));  

});     
      
    //simple functions for options on panel 
    function buttonOne(mesh, material) {
        console.log("button one is clicked");                             
        mesh.material = material;
    }

    function buttonTwo(mesh, material) {
        console.log("button two is clicked");                             
        mesh.material = material;
    }     

     function buttonThree(mesh, material) {
        console.log("button three is clicked");                             
        mesh.material = material; 
    }

    function buttonFour(mesh, material) {
        console.log("button four is clicked");  
        mesh.material = material;                          
    } 


    return scene;
        };
        /******* End of the create scene function ******/ 
    
        var scene = createScene(); //Call the createScene function

        // Register a render loop to repeatedly render the scene    
        engine.runRenderLoop(function () { 
            scene.render();
        });      
        
        // Watch for browser/canvas resize events
        window.addEventListener("resize", function () {
            engine.resize();
            
        });

</script>

</body>
</html>

Well. ImportMesh method works properly in the playground. What I can say from the first look is that your render loop and resize function are outside of the createScene function. So move those into the createScene function. So somewhere after actionManager.registerAction() for example.

Also, as your error says that it cannot import meshes. Maybe you didn’t import babylon-loaders?

https://doc.babylonjs.com/how_to/load_from_any_file_type

I was going by this tutorial which suggests having the render loop and resize function after the create scene function. I tried to move them but that gives me a blank page

https://doc.babylonjs.com/babylon101/first

Are you using npm modules ?

I haven’t but I could give it a go.

I tried swapping the babylonjs.loader.min.js for a local file and that just gave me a blank page

Okay. I might find a problem. Did you import babylonjs gui package?

I replicated your project. But I used npm packages. But I think it could be the same issue.

I imported GUI module like this

import * as GUI from ‘babylonjs-gui’;

And in your code GUI is created like this

var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI(“UI”);

And I got the same error message as you did. So Because I imported GUI as GUI as shown above, I don’t need the BABYLON part. So the working code is this.

var advancedTexture = GUI.AdvancedDynamicTexture.CreateFullscreenUI(“UI”);

And the error is gone (of course I needed to remove BABYLON. from every place I have GUI element).

Now, my guess is that you don’t need to remove BABYLON part if you imported gui package via CDN link. But if you haven’t do that, try to import it. And if it still doesn’t work try removing BABYLON part.

Before that, you can test if your scene is working when you completely remove GUI parts. So remove every part that contains BABYLON.GUI. And if it works we found the problem :slight_smile:

1 Like

Did the interactions start working with your version using npm packages?

I removed all the BABYLON.GUI sections but still got the unable to import meshes error.

I’ll try the import method next. Thanks again for all the help Sherlock Holmes :slight_smile:

1 Like

Everything is working properly on my end. Beside that GUI thing everything is working.

I am not sure what is going on on your end. I imported babylonjs, babylon-loaders and babylon-gui dependencies, and that’s it.

1 Like

I didn’t have much luck with npm modules but I added this link and now the GUI loads:

Only difference seems to be some unwanted transparency on the materials.

yourMaterial.backFaceCulling = false :slight_smile:

1 Like

ah yes, thanks again

1 Like

Hi @nogalo,

I don’t suppose you have any tips on how to close the GUI panel when one of the options is clicked?

Thank you!

https://www.babylonjs-playground.com/#YIU90M#118

Maybe something like this. Maybe not that elegant, but that’s the concept. I’ve expanded the button functions, and again with controlling switcher you control open/close animation of the GUI. .

1 Like

Perfect! Thanks again @nogalo for helping me out

1 Like