Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to make the nodes look at the camera at all times without calling refresh ? #39

Open
slechanii opened this issue Jun 2, 2022 · 4 comments

Comments

@slechanii
Copy link

slechanii commented Jun 2, 2022

Hi, i'm trying to make all my nodes (3D meshes) look at the camera position at all times, I am getting the camera position and making the mesh look at its position whenever its rendered the problem is that it only gets executed when nodeThreeObject is called, is there a way for me to make the nodes look at the camera at all times despite not getting rerendered ?

EDIT : After running some tests I found out that while I'm getting a camera, the vector is always (0,0,0) (even atfer moving the camera and triggering a rerender), here is the code I'm using to get the camera and print the position vector inside the nodeThreeObject :

  let cameraEl = document.querySelector("a-entity[camera]");
    if (!cameraEl) {
      cameraEl = document.querySelector("a-camera");
    }
    let camera = cameraEl.components.camera.camera;
    console.log(camera.position) // Always 0,0,0

Thanks.

@vasturiano
Copy link
Owner

@slechanii have you considered using Sprites? The very definition of a Sprite is a (planar) object that always faces the camera POV, which sounds like what you're seeking.

@slechanii
Copy link
Author

@slechanii have you considered using Sprites? The very definition of a Sprite is a (planar) object that always faces the camera POV, which sounds like what you're seeking.

Thanks for answering quickly!

I was indeed using sprites before and it did the job well, the problem is that from I what I saw, using sprites makes it impossible to use hover / onclick events due to a bug with aframe (gathered from past issues and having problems making it work, might have been fixed ?)

If it hasn't been fixed it means that I can't use sprites as I need hover & onclick on nodes for what i'm trying to achieve.

If sprites are not usable do you have an idea on how I could make the nodes always look at the camera ?

Currently I'm facing 2 issues :

  1. I can get a camera (explained in my 1st post) but this camera's position vector is always stuck at (0,0,0) no matter what I do strangely (am I doing it wrong ?)
  2. Even If I were to get the camera and the position vector right, the NodeThreeObject function seems to only be called on rerenders, so I would only be able to make the nodes look at the camera when a rerender is called

Thanks for your help!

@vasturiano
Copy link
Owner

@slechanii the reason behind the Sprite issue in Aframe is mentioned here: #31 (comment)

That issue eventually originates in the Aframe module, so there's not a whole lot that can be done to work around it.

Similarly, I'm also not sure how easy it would be to implement Sprite-like behaviour without using actual Sprite objects.

@micrology
Copy link

micrology commented Jun 11, 2022

I also encountered this problem and wrote a component to deal with it. Essentially what this does is to wrap each node in an A-Frame <a-sphere>, which makes dealing with them much more tractable. The code below also attaches a <a-text> entity to each sphere, and makes them always look towards the camera.

/* helper for vasturiano/3d-force-graph-vr
	*			draw a sphere around each force graph node, to make it easy to point to them with the ray caster,
	* 			and attach a text label (which rotates to always face the camera)
	* after the graph has been created, use something like
	*			fgEl.setAttribute('spherize', {})
	* to create the spheres
	*/
AFRAME.registerComponent('spherize', {
	schema: {},
	dependencies: ['forcegraph'],
	init: function () {
		// spheres are cached here and re-used
		this.spheres = new Map()
	},
	tick: function (time, timeDelta) {
		document
			.querySelector('[forcegraph]')
			.components.forcegraph.forceGraph.children.forEach((child) => {
				if (child.type == 'Mesh' && child.__data.id) {
					let sphereEl = this.spheres.get(child.__data.id)
					if (sphereEl) {
						// reuse existing sphere and label, but change its position
						sphereEl.object3D.position.copy(child.position)
					} else {
						sphereEl = document.createElement('a-sphere')
						sphereEl.classList.add('node')
						sphereEl.id = child.__data.id
						this.spheres.set(child.__data.id, sphereEl)
						sphereEl.setAttribute('position', child.position)
						let radius = child.geometry.parameters.radius + 0.1
						sphereEl.setAttribute('radius', radius)
						let color = child.__data.color || 'white'
						let compColor = lightOrDark(standardize_color(color)) == 'light' ? 'black' : 'white'
						sphereEl.setAttribute('color', color)
						this.el.appendChild(sphereEl)

						let label = document.createElement('a-entity')
						label.setAttribute('text', {
							value: splitText(child.__data.label, 9),
							color: compColor,
							width: 5 * radius,
							align: 'center',
						})
						sphereEl.setAttribute('look-at', '#cameraRig')
						label.setAttribute('position', {x: 0, y: 0, z: radius})
						sphereEl.appendChild(label)
					}
				}
			})
	},
})

(the line let compColor = lightOrDark(standardize_color(color)) == 'light' ? 'black' : 'white' sets compColor to white or black depending on which gives a better contrast with the colour of the sphere)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants