How can I make A-frame AR.js Marker tracking more stable and not hide the objects when marker not detected? - aframe

I am using AFrame and Ar.js with marker tracking.
The marker detection is quite bad, is there a way to improve it? also, is there a way to make the objects visible even if the marker is not being detected? keeping them in the same screen position for example.
This is my code
<script>
AFRAME.registerComponent('marker-handler', {
init: function () {
this.el.addEventListener('markerFound', () => {
this.el.setAttribute('visible', true);
});
this.el.addEventListener('markerLost', () => {
this.el.setAttribute('visible', true);
});
}
});
</script>
<body style="margin: 0; overflow: hidden;">
<a-scene
vr-mode-ui="enabled: false;"
loading-screen="enabled: false;"
renderer="logarithmicDepthBuffer: true;"
arjs="trackingMethod: best; sourceType: webcam; debugUIEnabled: false; sourceWidth:1280; sourceHeight:960; displayWidth: 1280; displayHeight: 960;"
id="scene"
embedded
gesture-detector
>
<a-assets>
<a-asset-item
id="animated-asset"
src="assets/asset.gltf"
></a-asset-item>
<a-asset-item
id="M-asset"
src="assets/M.gltf"
></a-asset-item>
</a-assets>
<a-marker
id="animated-marker"
type="pattern"
preset="custom"
url="assets/marker.patt"
raycaster="objects: .clickable"
emitevents="true"
cursor="fuse: false; rayOrigin: mouse;"
id="markerA"
>
<a-entity
id="bowser-model"
position="-0.7 0 0"
scale="0.5 0.5 0.5"
animation-mixer="loop: repeat"
gltf-model="#animated-asset"
rotation="0 30 -10"
class="clickable"
marker-handler
event-set__markerLost="_visible: true"
></a-entity>
<a-entity
id="M-model"
position="0.7 0 0"
scale="0.5 0.5 0.5"
gltf-model="#M-asset"
rotation="0 -30 -10"
animation-mixer="loop: repeat"
class="clickable"
marker-handler
event-set__markerLost="_visible: true"
></a-entity>
</a-marker>
<a-entity camera></a-entity>
</a-scene>
</body>
</html>
It looks like 'markerLost' attribute makes the object not visible even if visible is set to true.

Related

use animation-mixer for playing glb file, why not start from the beginning? It always start from the middle of the animation where I am using nft

Below is the code I use:
I use glb file and it should start animation from the beginning when marker is activated, but It start from the middle of the animation.
<div class="arjs-loader">
<div>Loading, please wait...</div>
</div>
<a-scene vr-mode-ui="enabled: false;"
renderer="logarithmicDepthBuffer: true;"
embedded arjs="trackingMethod: best; sourceType: webcam; debugUIEnabled: false;">
<a-assets>
<a-asset-item id="dja" src="./arfile2.glb"></a-asset-item>
</a-assets>
<a-nft type='nft' url='./demo/marker/djarum'
smooth='true' smoothCount='10' smoothTolerance='0.01' smoothThreshold='5'>
<a-entity gltf-model='#dja'
scale="90 90 90"
position="100 -200 0"
rotation="-90 0 0"
animation-mixer >
</a-entity>
</a-nft>
<a-entity camera>
</a-entity>
</a-scene>
<!--<a-scene>
<a-assets>
<a-asset-item id="tree" src="./tesla/scene.gltf"></a-asset-item>
</a-assets>
<a-entity gltf-model="#tree" scale="0.6 0.6 0.6" position="0 0.15 -3"></a-entity>
</a-scene>-->
The animation-mixer starts the animation when the model is loaded, not when it's visible.
You can wait until the image is detected, and then attach the animation-mixer:
const model = document.querySelector("[gltf]")
const nft = document.querySelector("a-nft");
nft.addEventListener("markerFound", () => {
model.setAttribute("animation-mixer", "")
})

AFrame Animation Start\End Condition

I'm trying to get the cylinder to begin and pause rotating on mouse click but I can only get it to begin, not stop. I'm not sure what else would work? When I add the end condition it stops working all together. My question is, how can I get the begin and end conditions to be the same input?
<<html>
<head>
<script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-environment-component/dist/aframe-
environment-component.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/annyang/2.5.0/annyang.min.js">
</script>
<script src="/components/voice-commands.js"></script>
</head>
<body>
<button>Go</button>
<a-scene>
<a-assets>
<img id="sky" src="sky2.0.jpg">
</a-assets>
<a-camera position="0 1.6 0">
<a-cursor></a-cursor>
</a-camera>
<a-box position="0 -2 0" color="grey" scale="30 2.5 20"></a-box>
<a-cylinder id="center" position="17 0 0" color="grey" scale="4 4 4"
rotation="0 0 90">
<a-sphere position="0 -0.375 0" color="grey" scale="1 1 1"
rotation="0 0 90"></a-sphere>
<a-box position="8 0 0" color="grey" scale="15 0.25 1" rotation="-20
0 0"></a-box>
<a-box position="-3.5 0 6" color="grey" scale="15 0.25 1"
rotation="-20 240 0"></a-box>
<a-box position="-3.5 0 -6" color="grey" scale="15 0.25 1"
rotation="-20 -240 0"></a-box>
<a-animation attribute="rotation" begin="click" end="click"
dur="20000" to="360 0 90" repeat="indefinite" easing="linear"></a-animation>
</a-cylinder>
<a-sky src="#sky"></a-sky>
<a-cylinder position="0 -402 0" color="grey" scale="5 800 5" rotation="0 0
0">
<a-entity id="annyang" annyang-voice-recognition></a-entity>
</a-scene>
</body>
</html>
I would threw the logic to a seperate a-frame component.
Having a setup like this:
<a-cylinder animation-controller
animation="resumeEvents: start; pauseEvents: pause">
</a-cylinder>
When clicked - switch a boolean, and depending on the value start or stop the animation:
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("animation-controller", {
init: function() {
this.running = false
this.el.addEventListener("click", (e) => {
if (!this.running) {
this.el.emit("start") // animation not running - start it
} else {
this.el.emit("pause") // animation running - stop it
}
this.running = !this.running // flip the flag
})
}
})
</script>
<a-scene cursor="rayOrigin: mouse">
<a-cylinder animation-controller position="0 1 -3" color="red"
animation="property: rotation; to: 0 0 360; dur:1000; loop: true; resumeEvents: start; pauseEvents: pause">
</a-cylinder>
</a-scene>
Original answer - for aframe < 1.0.0
I would threw the logic to a seperate a-frame component.
Having a setup like this:
<a-cylinder anim-controller>
<a-animation begin="start" end="stop" (...)></a-animation>
</a-cylinder>
When clicked - switch a boolean, and depending on the value start or stop the animation:
AFRAME.registerComponent("anim-controller", {
init: function() {
this.running = false
this.animComp = document.querySelector("a-animation")
this.el.addEventListener("click", (e) => {
if (!this.running) {
this.animComp.emit("start") // animation not running - start it
} else {
this.animComp.emit("stop") // animation running - stop it
}
this.running = !this.running // flip the flag
})
}
})
Simple - if the animation is running - start it. Otherwise stop it.
Fiddle here.

Aframe - How to Retrieve the text from textarea in Aframe

I Am unable to retrieve the text from the textarea as I just want to scale a box by typing the x-factor as the input. But the input is not being retrieved for some reason. Please help!...here is the code:
<html>
<head>
<title>My A-Frame Scene</title>
<script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-textarea-component/dist/aframe-textarea-component.min.js"></script>
</head>
<body>
<a-scene>
<a-sky color="#6EBAA7"></a-sky>
<a-box id="redbox" position="3 2.5 -2" color="red"></a-box>
<a-entity position="0 1 -2" id="inputText" textarea="backgroundColor:white; color:black; cols:5; rows:1;value:2;"></a-entity>
<a-input postion="0 2 -2"></a-input>
<a-text id="outputText" position="0 2 -2" value="Hello"></a-text>
<a-camera position="0 1 1">
<a-cursor color="yellow"></a-cursor>
</a-camera>
<a-entity id="select-button" geometry="primitive: box" material="color:red" position="0 0 -2"></a>
</a-scene>
<script>
var box = document.querySelector('#redbox')
var printText = document.querySelector('#inputText');
document.querySelector('#select-button').addEventListener('click',function(){
box.setAttribute("scale",
{
x : parseInt(printText.getAttribute('textarea').value),
y : 1.5,
z : 1.5
}
);
});
</script>
</body>
</html>
Edit: I tried changing the code in javascript as follows:
parseInt(printText.getAttribute('textarea').text
It still does not work!!
The textarea component does not expose the value directly but you can do:
document.querySelector('#inputText').components.textarea.textarea.value
The latest version (0.3.0) of aframe-textarea-component now includes a getText() method which you can use to get the current text.
document.querySelector('#inputText').components.textarea.getText();

How to control children entity shadows?

I am running into issues where I parent entities and set certain attributes, such as shadows, on the parent and its children (different values). It appears the object3Dset event bubbles and anything the child (or when any other child triggers the object3dset event) is overwritten by the parent (as it sets all children to its value automatically: https://github.com/aframevr/aframe/blob/master/src/components/shadow.js ). Is there a standardized way to handle this behaviour?
Here is the "hello aframe" example to simplify:
<!DOCTYPE html>
<html>
<head>
<title>Hello, WebVR! - A-Frame</title>
<meta name="description" content="Hello, WebVR! - A-Frame">
<script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
</head>
<body>
<a-scene>
<a-entity light="type:directional; castShadow:true;" position="1 1 1"></a-entity>
<!-- parent that shadows -->
<a-entity shadow="cast:true; receive:true">
<!-- children that avoid shadows -->
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow="cast:false; receive:false"></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow="cast:false; receive:false"></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow="cast:false; receive:false"></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow="cast:false; receive:false"></a-plane>
</a-entity>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
Shadows :(
To work around this issue I had to remake the shadow component to have a flag to control if children are also affected. Would be curious to hear if there is a better way to do this.
AFRAME.registerComponent('custom-shadow', {
schema: {
cast: {type: 'boolean', default: true},
receive: {type: 'boolean', default: true},
applyToChildren: {type: 'boolean', default: true}
},
multiple: false, //do not allow multiple instances of this component on this entity
init: function() {
this.applyShadow();
this.el.addEventListener('object3dset', this.applyShadow.bind(this));
},
applyShadow : function () {
const data = this.data;
const mesh = this.el.getObject3D('mesh');
if (!mesh) return;
mesh.traverse(function (node) {
node.castShadow = data.cast;
node.receiveShadow = data.receive;
if (data.applyToChildren) {
return;
}
});
},
update: function (oldData) {
this.applyShadow.bind(this);
}
});

aframe raycaster not registering in browser

I try to work with a raycaster, but i can't seem te get it to work(nothing shows in my console). I tried the example on: https://aframe.io/docs/0.5.0/components/raycaster.html
Javascript file
AFRAME.registerComponent('collider-check', {
dependencies: ['raycaster'],
init: function () {
this.el.addEventListener('raycaster-intersected', function () {
console.log('Player hit something!');
});
}
});
index.html
<a-scene>
<a-entity id="player" collider-check>
<a-entity raycaster="objects: .collidable" position="0 -0.9 0" rotation="90 0 0"></a-entity>
</a-entity>
<a-entity class="collidable" geometry="primitive: box" position="1 0 0"></a-entity>
</a-scene>
I try to walk in al possible directions and nothing shows in my console. I run my code in google chrome on a xampp server.
Hope someone can explain to me what is going on!
Try this structure. Make sure the component is registered before the A-Frame HTML.
<html>
<head>
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="collider-check.js"></script>
</head>
<body>
<a-scene>
<a-entity collider-check></a-entity>
</a-scene>
</body>
</html>

Resources