add button to 3d model in babylon.js - babylonjs

I'm a newbie in babylon.js and I'm trying to create something exactly like that
https://sketchfab.com/3d-models/anatomy-of-the-human-brain-2019-4870387fd52b4706a87b1f3337356579
I had imported the model and add a button to the scene when I click on it the data appear as expected.
the problem is the button in away from the model.
here's waht i did.
https://www.babylonjs-playground.com/#JUKXQD#341
is there any better way than what i did ?

The buttons on sketchfab are not 3D buttons but HTML buttons added as an HTML layer on top of the canvas that renders the 3D. on each frame the HTML position is updated.
Something along the lines of this:
https://www.babylonjs-playground.com/debug.html#JUKXQD#344
Notice that the projection is happening on each frame:
scene.onBeforeRenderObservable.add(() => {
pointArray.forEach(point => {
// project from the stored picked point to the 2D screen)
var p = BABYLON.Vector3.Project(point.position,
BABYLON.Matrix.Identity(),
scene.getTransformMatrix(),
camera.viewport.toGlobal(scene.getEngine().getRenderHeight(), scene.getEngine().getRenderHeight()));
point.element.style.top = (p.y - point.element.offsetHeight / 2) + "px";
point.element.style.left = (p.x + point.element.offsetWidth) + "px";
})
});

Related

How to place a Plane geometry button on top of a row of cubes without displacing the button along z axis?

I have a grid of cubes and I need to show a button while hovering over a row. I have created the button with a plane geometry but while setting is position.y such that it stands right on top of the row, it displaces along the z axis and appears against another row at the back giving the wrong appearance. Please see the code below for the button and also a screenshot of the issue. The white button should appear right on top of the orange row.
I have tried to do what this post talks about. But inspite of all that, I am unable to fix it. Not sure why making the object high enough should affect the depth. I have not added any translation or rotation to it. Please help.
function createButtonForEachRow(numCols, width, offsetX, rowIndex, cubeSize , cubePadding)
{
var geometry = new THREE.PlaneGeometry( width, 1);
var material = new THREE.MeshBasicMaterial( {map: buttonTexture, side:THREE.DoubleSide } );
material.map.minFilter = THREE.LinearFilter;
var plane = new THREE.Mesh(geometry, material);
var referenceZPos = 1- (rowIndex * (cubeSize + cubePadding));
plane.position.x = offsetX + cubeSize + cubePadding * numCols + 3 ;
plane.position.y = cubeSize*3;
plane.position.z = referenceZPos;
// plane.rotation.x = 3 * Math.PI/2;
plane.visible = false;
return plane;
}
Screenshot
Creating 2 scenes and adding the button on the second scene worked. The link mentioned in the question actually worked. I had forgotten to set autoclear to false.

On mesh click, ArcRotateCamera focus on

I'm using ArcRotateCamera, when I click on mesh, I have to focus camera on
var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2, 300, BABYLON.Vector3.Zero(), scene);
camera.setTarget(BABYLON.Vector3.Zero());
// on mesh click, focus in
var i = 2;
var pickInfo = scene.pick(scene.pointerX, scene.pointerY);
if (pickInfo.hit) {
pickInfo.pickedMesh.actionManager = new BABYLON.ActionManager(scene);
pickInfo.pickedMesh.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger,
function (event) {
camera.position = (new BABYLON.Vector3(pickInfo.pickedPoint.x, pickInfo.pickedPoint.y, camera.position.z + i));
i += 2;
})
);
}
this code changes mesh's z position but don't makes it in the center of screen
There are a few things that can be changed in your code.
1st - what you are doing is executing a code action after a click, instead of simply running the code in the callback after a pick has occurred. You are registering a pick action (technically user click) on right on the first frame, but only if the mouse was found in the right location at the right moment. My guess is that it didn't work every time (unless you scene is covered with meshes :-) )
2nd - you are changing the camera's position, instead of change the position to which it is looking. Changing the camera's position won't result in what you want (to see the selected mesh), it will move the camera to a new position while still focusing on the old position.
There are a few ways to solve this. The first is this:
scene.onPointerDown = function(evt, pickInfo) {
if(pickInfo.hit) {
camera.focusOn([pickInfo.pickedMesh], true);
}
}
The ArcRotate camera provides focusOn function that focuses on a group of meshes, while fixing the orientation of the camera. this is very helpful. You can see a demo here:
https://playground.babylonjs.com/#A1210C#51
Another solution would be to use the setTarget function:
https://playground.babylonjs.com/#A1210C#52
Which works a bit differently (notice the orientation change of the camera).
Another thing - use the pointer events integrated in Babylon, as they are saving you the extra call for a scene pick. pointer down is executed with the pickinfo integrated in the function, so you can get the picking info of the current pointer down / up / move each frame.
**** EDIT ****
After a new comment - since you want to animate the values, all you need to do is store the current values, calculate the new ones, and animate the values using the internal animation system (documentation here - https://doc.babylonjs.com/babylon101/animations#basic-animation) . There are many ways to achieve this, I took an old function and modernized it :-)
Here is the demo - https://playground.babylonjs.com/#A1210C#53

openlayers mouse position problem with css transform:scale()

I need to scale my Openlayer layer map in order to display full map [ fit to screen ] while print preview is set [ A4, A3 ... A0 page setup ]. Now after apply the css transform : scale() to the map div the mouse over position is not working properly.
I found a issues in but didn't found any solution.
ol issues #5747
Any suggestion?
I had the same problem. But my situation was on "click query" and it goes to offset.
My solution is as follows. Hope it a little help.
var px = evt.pixel[0]/0.8;
var py = evt.pixel[1]/0.8;
var geoPt = map.getCoordinateFromPixel([px,py]);
var x = geoPt[0];
var y = geoPt[1];

How would I make a color relative to images in the background?

For example, if you go to Twitter and click on an image, you can see they have a nice color that is close to what you see on the image. I tried looking up ways to achieve this as well as trying to figure it out on my own but no luck. I'm not sure if there's a color: relative property or not.
if you want to use the a colour that exists in your image and set it as a background colour you need to use the canvas element in the following manner:
HTML (this is your image)
<img src="multicolour.jpg" id="mainImage">
JS
window.onload = function() {
// get the body element to set background (this can change dependending of your needs)
let body = document.getElementsByTagName("body")
// get references to the image element that contains the picture you want to match with background
let referenceImage = document.getElementById("mainImage");
// create a canvas element (but don't add it to the page)
let canvas = document.createElement("canvas");
// make the canvas size the same as your image
canvas.width = referenceImage.offsetWidth
canvas.height = referenceImage.offsetHeight
// create the canvas context
let context = canvas.getContext('2d')
// usage your image reference to draw the image in the canvas
context.drawImage(referenceImage,0,0);
// select a random X and Y coordinates inside the drawn image in the canvas
// (you don't have to do this one, but I did to demonstrate the code)
let randomX = Math.floor(Math.random() * (referenceImage.offsetWidth - 1) + 1)
let randomY = Math.floor(Math.random() * (referenceImage.offsetHeight - 1) + 1)
// THIS IS THE MOST IMPORTANT LINE
// getImageData takes 4 arguments: coord x, coord y, sample size w, and sample size h.
// in our case the sample size is going to be of 1 pixel so it retrieves only 1 color
// the method gives you the data object which constains and array with the r, b, g colour data from the selected pixel
let color = context.getImageData(randomX, randomY, 1, 1).data
// use the data to dynamically add a background color extracted from your image
body[0].style.backgroundColor = `rgb(${color[0]},${color[1]},${color[2]})`
}
here is a gif of the code working... hopefully this helps
UPDATE
Here is the code to select two random points and create a css3 background gradient
window.onload = function() {
// get the body element to set background (this can change dependending of your needs)
let body = document.getElementsByTagName("body")
// get references to the image element that contains the picture you want to match with background
let referenceImage = document.getElementById("mainImage");
// create a canvas element (but don't add it to the page)
let canvas = document.createElement("canvas");
// make the canvas size the same as your image
canvas.width = referenceImage.offsetWidth
canvas.height = referenceImage.offsetHeight
// create the canvas context
let context = canvas.getContext('2d')
// usage your image reference to draw the image in the canvas
context.drawImage(referenceImage,0,0);
// select a random X and Y coordinates inside the drawn image in the canvas
// (you don't have to do this one, but I did to demonstrate the code)
let randomX = Math.floor(Math.random() * (referenceImage.offsetWidth - 1) + 1)
let randomY = Math.floor(Math.random() * (referenceImage.offsetHeight - 1) + 1)
// THIS IS THE MOST IMPORTANT LINE
// getImageData takes 4 arguments: coord x, coord y, sample size w, and sample size h.
// in our case the sample size is going to be of 1 pixel so it retrieves only 1 color
// the method gives you the data object which constains and array with the r, b, g colour data from the selected pixel
let colorOne = context.getImageData(randomX, randomY, 1, 1).data
// THE SAME TO OBTAIN ANOTHER pixel data
let randomX2 = Math.floor(Math.random() * (referenceImage.offsetWidth - 1) + 1)
let randomY2 = Math.floor(Math.random() * (referenceImage.offsetHeight - 1) + 1)
let colorTwo = context.getImageData(randomX2, randomY2, 1, 1).data
// use the data to dynamically add a background color extracted from your image
//body[0].style.backgroundColor = `rgb(${allColors[0]},${allColors[1]},${allColors[2]})`
body[0].style.backgroundImage = `linear-gradient(to right, rgb(${colorOne[0]},${colorOne[1]},${colorOne[2]}),rgb(${colorTwo[0]},${colorTwo[1]},${colorTwo[2]}))`;
}
The following are your options.
1. Use an svg.
As far as I know there's no way to have javascript figure out what color is being used in a png and set it as a background color. But you can work the other way around. You can have javascript set the background color and an svg image to be the same color.
See this stackoverflow answer to learn more about modifying svgs with javascript.
2. Use a custom font.
There are fonts out there that provide a bunch of icons instead of letters, you can also create your own font if you feel so inclined to do so. With css you just have to set the font-color of that icon to be the same as the background-color of your other element.
Font Awesome provides a bunch of useful custom icons. If the image you need to use happens to be similar to one of theirs, you can just go with them.
3. Use canvas
If you really want to spend the time to code it up you can use a html <canvas/> element and put the image into it. From there you can inspect certain details about the image like its color, then apply that color to other elements. I won't go into too much detail about using this method as it seems like it's probably overkill for what you're trying to do, but you can read up more about from this stackoverflow answer.
4. Just live with it.
Not a fun solution, but this is usually the option I go with. You simply have to hard-code the color of the image into your css and live with it. If you ever need to modify the color of the image, you have to remember to update your css also.

parentNode is null or undefined in draggable resizeable custom overlay with image. GMaps v3

I have made a resizeable custom overlay with an image and it works, you can see it here,
debug. In the upper right hand corner and lower left corner there are some red corner icons that you can drag and resize the image and overlay. The problem is that when you make the image smaller, it leaves behind remnants of the lager scale images.
I then added a function to remove the overlay before drawing the new one, straight out of the example, overlay.onRemove(); It worked, but as soon as you un click the marker it gives the error "parentnode: object is null or undefined", you can see it here debug2
If you view the source on them, the drag listener in on line 173 and calls the overlay = new USGSOverlay(bounds, srcImage, map, 'yes'); on line 194.
The function is on line 71. and looks for ifDrag and then deletes the previous overlay, I am baffled as to why it works but as soon as you un click the marker it throws the error and breaks the script.
It would be awesome if someone could provide me with a fix or workaround to the error. This is a very nice feature to have and am too close to give up. Thanks.
I believe your problem is in the draw function:
var div;
USGSOverlay.prototype.draw = function() {
// Size and position the overlay. We use a southwest and northeast
// position of the overlay to peg it to the correct position and size.
// We need to retrieve the projection from this overlay to do this.
var overlayProjection = this.getProjection();
// Retrieve the southwest and northeast coordinates of this overlay
// in latlngs and convert them to pixels coordinates.
// We'll use these coordinates to resize the DIV.
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
// Size the image's DIV to fit the indicated dimensions.
div = this.div_;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 'px';
div.style.width = (ne.x - sw.x) + 'px';
div.style.height = (sw.y - ne.y) + 'px';
}
Don't you want to remove the external declaration of var div and make the references within the draw function reference this.div? I believe that is your problem.
Shouldn't the last lines of draw be implemented like this:
//Remove this line:
//div = this.div_;
this.div_.style.left = sw.x + 'px';
this.div_.style.top = ne.y + 'px';
this.div_.style.width = (ne.x - sw.x) + 'px';
this.div_.style.height = (sw.y - ne.y) + 'px';
You also have similar code in the add function. I suggest that you make the code in add similar - just work directly with this.div_; it looks like you might be getting a reference mixed around.
We came to the conclusion that too many events were fired and killed the script, 'dragend' was the solution (Not perfect but works), works in all browsers and have set opacity at 50% to get the perfect alignment to the location. Appreciate your help. Cheers.

Resources