Wrong position of a clickable object on AR.js scene - aframe

I have a simple AR.js scene that should display a clickable white box on a hiro marker. After click the box's colour should change to red. In fact, the box is clickable and the colour is changing, but it's position and the position of it's clickable area are not the same. In my case this area is beneath the box. Here's a code example:
<!DOCTYPE html>
<html>
<script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
<script src="https://rawgit.com/jeromeetienne/ar.js/master/aframe/build/aframe-ar.js"></script>
<script>
AFRAME.registerComponent('clickhandler', {
init: function () {
this.el.addEventListener('click', () => {
this.el.setAttribute('material', 'color: red;');
});
},
});
</script>
<body>
<a-scene embedded arjs>
<a-marker cursor="rayOrigin: mouse;" preset="hiro">
<a-box
material="color: white;"
position="0 0 0"
depth="0.2"
height="0.01"
width="0.2"
clickhandler
/>
</a-marker>
<a-entity camera></a-entity>
</a-scene>
</body>
</html>
However, if you open the aframe inspector (Ctrl + Alt + I) and then close it, box's clickable area and it's position will be correct and I don't understand how is that happening. So how can I make this scene to display the right way?

I've explored how the aframe inspector works and found that it inserts a new raycaster programmatically after it's initialization, so I've decided to do the same and it worked!
All you need to do to place clickable area in the same spot with it's object is to execute these lines after the scene is fully initialized.
const scene = AFRAME.scenes[0];
if (!scene) {
return;
}
const mouseCursor = document.createElement('a-entity');
mouseCursor.setAttribute('cursor', 'rayOrigin', 'mouse');
scene.appendChild(mouseCursor);

Related

A-frame click event doesn't work for object intersecting cursor when entering VR

I have created an A-frame scene, and I am using a large invisible sphere to catch any mouse click to change an image. This works perfectly before entering VR mode, however, on entering VR mode, the click events are not caught until you look away from the object and look back.
From reading the documentation my guess is that this isn't counting the cursor as intersecting the object if it starts the scene in that position?
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent('clickhandler', {
init: function () {
this.el.addEventListener('mousedown', function (evt) {
document.querySelector("#sky").setAttribute("src", "photo1.jpg");
});
}
});
</script>
<a-scene embedded background="color: black">
<a-entity camera look-controls position="0 0 0">
<a-cursor visible="false"></a-cursor>
</a-entity>
<a-sky id="sky" src="photo-initial.jpg"></a-sky>
<a-entity id="circle" cursor-listener geometry="primitive: circle; radius: 25" visible="false" material="color: blue" clickhandler position="-2.5 0.25 -1.5" rotation="0 15 0"></a-entity>
</a-scene>
So when this scene starts, the image is in front of the view, a large invisible sphere fills the whole view - from the browser, I can immediately click to trigger the click event. But if I reload the page and go straight into VR and click, nothing happens until I click to look 180 degrees away, and then look back. Then if I click, the event triggers.

How to create a UI link in A-frame

I am looking to add a piece of text to the camera on an A-frame scene which will act as a link to transfer to a non-VR page.
<a-entity camera look-controls position="0 0 0">
<a-cursor visible="false">
</a-cursor>
<a-entity text="value: Galleries; color: black; width: 2;" position="-0.25 0.7 -1" onClickLink>
</a-entity>
</a-entity>
The onClickLink function is registered and uses window.location.href = "https://www.google.com"; to change page as suggested in the docs.
I have created a gallery, and want to give users a simple UI link at the top to take them back to a list of other galleries - however - in the example above the onClickLink function just gets applied to the whole scene rather than only the piece of text. I guess it has maybe been applied to its parent - the camera - so any click counts as firing the event? Or do I need to add something to the event to determine what object was clicked? I'm not sure how raycasting would work on something in the top left corner of the camera?
I do not want to use a 3D object in the scene, and I similarly don't want to use the Link portal entity that will be placed in the scene (I did try going down this route similar to the above, and it again just applied the link to the entire scene rather than on clicking the link itself).
Is there a way to achieve what I'm after?
You can use standard html elements and a-frame element to gather and use Css (position: fixed; z-index: 999) to bring it forward to the top but to create an external html link I share part of my code hope you find the idea, you can use jquery(or pure javascript) to add click event to your a-frame objest, but first add below code to <a-camera tag:
raycaster="far: 10000; objects: .clickable"
then add "clickable" class name to any A-frame object you want to be clicked by user
<a-image visible=true id="web" class="clickable external" src="#imgweb" alpha-test="0.5" position="-0.14 -0.9 0" height="0.18" width="0.18"></a-image>
and then like any html dom object you can access to aframe objects by javascript:
window.onload = function() {
$(".external").each(function(index) {
$(this).on("click", function() {
console.log(this.id + ' Pushed');
switch (this.id.toLowerCase()) {
case 'web':
window.open("http://www.farsix.com/");
break;
case 'instagram':
window.open("https://www.instagram.com/");
break;
default:
console.log("Unknown Link!");
};
});
});
};

Aframe ambient light not work with gltf model

I tried a easy aframe demo with glft
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
<script src="../../../dist/aframe-master.js"></script>
<script src="js/aframe-extras.loaders.min.js"></script>
</head>
<body>
<a-scene >
<a-entity light="type: ambient; intensity: 0.2"></a-entity>
<a-entity
gltf-model="assets/cow02.glb"
scale="100 100 100"
rotation='0 0 0'
animation="property: rotation; to: 0 360 0; loop: true; dur: 20000"
position="-2 1 -5"
>
</a-entity>
</a-scene>
</body>
</html>
and now the issue is this ambientlight <a-entity light="type: ambient; intensity: 0.2"></a-entity> not working ,I mean nothing change after add this light.
I'm not sure how the gltf-loader decides what material to use, but it seems Your models material is simple enough to be interpreted as a MeshBasicMaterial. According to the docs - it is not affected by any lights.
Probably if You'd set the metalness, roughness, add a normal map - the loader would use a MeshStandardMaterial (or any other with those properties - which are affected by lighting).
If you want to change the material using three.js, you could write a component like this:
// wait until the model is loaded
this.el.addEventListener("model-loaded", e => {
// grab the mesh
let model = this.el.getObject3D("mesh");
// find the node with the basic material
model.traverse(function(node) {
// ignore bones and other nodes without any material
if (!node.material) return;
// keep the reference to the old material - we want to dispose it later
var tmp = node.material
// substitute the material
node.material = new THREE.MeshStandardMaterial({
skinning: true, // the original material is using skinning
map: node.material.map // we want the original texture
});
// update and clean up
node.material.needsUpdate = true;
tmp.dispose()
}
})
Check it out in this glitch.

adding object dynamically in live a-frame camera view

I am trying to place an image in a-frame camera view. please share an example.
A quick way to do this is to add an invisible "marker" as a child of the camera, and use its position as the spawn point when adding an object.
HTML
<a-scene>
<a-camera>
<a-entity id="marker" position="0 0 -5"></a-entity>
</a-camera>
<a-cylinder id="floor" height="0.1" radius="10" color="green"></a-cylinder>
</a-scene>
JS
var sceneEl = document.querySelector('a-scene');
var markerEl = document.querySelector('#marker');
// Add boxe when spacebar is pressed.
document.addEventListener('keyup', function (e) {
if (e.keyCode !== 32) return;
var newEl = document.createElement('a-box');
newEl.setAttribute('color', 'red');
sceneEl.appendChild(newEl);
var position = markerEl.object3D.getWorldPosition();
position.y = 0.5;
newEl.setAttribute('position', position);
});
Codepen: https://codepen.io/donmccurdy/pen/QOOXbK?editors=1010

How to detect when video texture is done in A-Frame?

I want to transition out of the videospheres once the video ends, is there any event that is triggered when a video is over or some other way to go about doing this?
<a-scene>
<a-assets>
<video id="vid" src="a.mp4"></video>
</a-assets>
<a-videosphere src="#vid"></a-videosphere>
</a-scene>
Use the events from the native video element.
var video = document.getElementById('vid);
video.addEventListener('ended', function (evt) {
// ...
});

Resources