How to position a html button to the position of an objects Vector3 converted to screen space?

Hi Everyone,

My first post and brand new to Babylon.js, I’m a 3D artist with some knowledge of html and css, but very little if none javascript.

Anyways on too my question, I have started with the default playground scene. which I have created a button with the following

 var threeDButton = document.createElement("button");

            threeDButton.className = "label-component";
            threeDButton.className += " upper";
            threeDButton.style.display = "block";
            threeDButton.style.position = "absolute";
            threeDButton.style.top = "0";
            threeDButton.style.left = "0";
            threeDButton.style.transform = "translate3d(50vw, 50px, 0px)";

            var threeDButtonDot = document.createElement("span");

            threeDButtonDot.className = "dot-circle";

            threeDButton.appendChild(threeDButtonDot);

            var threeDButtonText = document.createElement("span");

            threeDButtonText.textContent = "Handle";

            threeDButton.appendChild(threeDButtonText);
            document.body.appendChild(threeDButton);

which I want to position the button onto a certain I want to say null object is 3d space. so I am trying to convert the vector3 of that object to screen space?

which I have found the follow code

var vertex2 = new BABYLON.Vector3();  

         var vertex2_world = BABYLON.Vector3.TransformCoordinates(vertex2, boxSml.getWorldMatrix());


          scene.registerBeforeRender(function(vertex2_view) {
              var vertex2_view = BABYLON.Vector3.Project(
                  vertex2_world, BABYLON.Matrix.Identity(),
                  scene.getTransformMatrix(),
                  camera.viewport.toGlobal(engine.getRenderWidth(true), engine.getRenderHeight(true))
              );

              threeDButton.addEventListener("click", () => {
                console.log('vertex2_view');
                console.log(vertex2_view);
                
            });

          });

of which you can see I have an EventListener which console.logs the vertex2_view. which seems to give me some sort of screen space x,y,z. but for some reason one click gives hundreds of the same, not sure why it doesn’t just give one per click? is there a reason why it give hundreds of lines of the same values.

anyways it not a major problem just wondering why, to test if the x,y,z where correct position, I inspected the button in the chrome inspector and done a little css with the value of the console.log for instance the following

transform: translate3d(calc(1279.5000014331736px - 50%) , calc(264.1972926830056px - 50%), 0);

which places the button exactly where I want it to be, So is this the best way to get to this point so far or is there a better way of doing this? But also how will I continue this to automatically send these x,y,z to my css transform: translate3d ?

Kind regards,
FireMe

Hi @FireMe,

Welcome aboard!

You have several hundreds of logs because you register your listener each time registerBeforeRender is called, and this is called before each frame. You should registere the listener a single time.

Have a look at this PG, I have taken your code:

https://playground.babylonjs.com/#YU7J32

When you click on the button, it will snap to the center of the sphere.

Hi @Evgeni_Popov thanks for the welcome and your response and the explanation regarding the hundreds of logs and the reason for this happening.

Also thanks a lot for the playground link that you setup for me, I have now created a modified version which can be found here - https://playground.babylonjs.com/#YU7J32#2

The button click to change it location was just a temporary thing for testing, ideally I would like it to automatically just stick to that point? or even if you are dragging then the button would be display none, and then once dragging had stoped then the button would be at that position.

But for now I thought I might of been able to just add the following to my code for the transform translate of the button its self like the following

instead of this which is the default

threeDButton.style.transform = "translate3d(50vw, 50px, 0px)";

I thought I could change it to this?

threeDButton.style.transform = "translate3d(calc(" + (vertexScreenCoords.x + ofstX) + "px - 50%), calc(" + (vertexScreenCoords.y + ofstY) + "px - 50%), 0px)";

But I get the error - Line 69 : 78 - Cannot read property ‘x’ of undefined

Which I guess is due to my lack of knowledge how programming/javascript in general works. so advice is gratefully appreciated.

P.S Is there a way I can use CSS in the playground if I use tag it just doesn’t run.

@Evgeni_Popov

Just been playing about and some research managed to get it working how I wanted it on the following link.

https://playground.babylonjs.com/#YU7J32#3

Not sure if it ideal for performance etc, Would be great if you could take a look and check to see if it ideal or not thanks.

It seems good to me.

Depending on your needs, you may also use the GUI to apply labels to meshes.

@Evgeni_Popov Thanks for your feedback will have to check out the GUI labels, I just wanted to stick to html and css as Im familiar with it. with regards to the GUI is that actually in the 3d canvas element, as don’t these struggle with crisp fonts etc?

The GUI is handled by Babylon thanks to canvas.getContext("2d") on a HTML canvas element, so everything available on this context is also available to you (notably, the same fonts, colors, … than in HTML).

Yer came across this demo but the text content seems a little blurred compared to just html

https://www.babylonjs.com/demos/gui/