Aframe - draw in a-sphere or videosphere or a-sky - aframe

How can make a function like in this sample:
https://codepen.io/knee-cola/pen/OpmYyp.
I am reciceving this error:
Uncaught TypeError: Cannot read property 'setFromCamera' of null.
This is my code:
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width">
<title>EXPERIMENT ONLY</title>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="aframe.js"></script>
<script type="text/javascript">
$(document).ready(function() {
document.addEventListener('mousemove', onMouseMove, false);
});
function onMouseMove(ev) {
var sceneEl = document.querySelector('a-scene');
ev.preventDefault();
ev.cancelBubble = true;
var app = this;
var threePoint = _findIntersection(sceneEl, ev.clientX, ev.clientY);
}
function _findIntersection(app, clientX, clientY) {
var raycasterEl = AFRAME.scenes[0].querySelector('[raycaster]');
var sphereEl = document.querySelector('#bilog');
var cameraEl = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// converting click coordinates from document-space to Three space
var mouseRay = new THREE.Vector2();
mouseRay.x = ( clientX / window.innerWidth ) * 2 - 1;
mouseRay.y = - ( clientY / window.innerHeight ) * 2 + 1;
// intersecting mouse ray with all the objects in the scene
raycasterEl.setFromCamera(mouseRay, cameraEl);
var intersects = raycasterEl.intersectObjects( sphereEl );
if(intersects.length===0) {
return(null);
}
return(intersects[0].point);
}
</script>
</head>
<body>
<a-scene>
<a-assets>
<canvas id="my-canvas" crossorigin="anonymous"></canvas>
</a-assets>
<a-sphere id="bilog" radius="1" material="src: #my-canvas; side:front" draw-canvas="my-canvas" position="0 0 0"></a-sphere>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
I have tried very hard but still no success. I don't know three.js and have a little knowledge in Aframe. Please help me. Thank you.

You have to wait until the scene has loaded:
document.querySelector(‘a-scene’).addEventListener(‘loaded’, function() { ... your code ... });
There’s no entity with a raycaster entity in your scene so document.querySelector(‘[raycaster]’) will return null
You also need to call the function on the component, not the entity:
raycasterEl.components.raycaster.setFromCamera(...)
There might be other problems. I recommend reading through the A-Frame docs to get you acquainted with the scene and components APIs

Related

How to get real position in space of the entity in aframe?

I have tried multiple ways to get the position of the element immediately after animation but every time I am receiving a hard-coded position of the entity that I have entered. I have used tick function to track the object all the time. These are the following ways I have tried to keep a track of current location of the entity but of them are returning a position that I have assigned to the entity. Is it a bug in aframe 1.2.0 because in previous versions its working fine.
Object.values(el.getAttribute('position'))
el.object3D.animations
Object.values(document.querySelector('#duck1').object3D.getWorldPosition(new THREE.Vector3()))
Object.values(el.object3D.getWorldPosition(new THREE.Vector3()))
For the reference I have atttached the live code as well. Right after mouseenters into cube the animation will trigger. After the end of animation location of the entity will be shown in console.log entry
The Code:
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent('eventhandling', {
tick: function () {
var el = this.el;
var entity = document.querySelector('#duck1');
el.addEventListener('animationcomplete', function(){
console.log("getAttribute: "+Object.values(el.getAttribute('position')));
console.log("object3D.position: "+(el.object3D.animations));
console.log("document.querySelector: "+ Object.values(document.querySelector('#duck1').object3D.getWorldPosition(new THREE.Vector3())));
console.log("object3D.getWorldPosition(new THREE.Vector3()): "+Object.values(el.object3D.getWorldPosition(new THREE.Vector3())));
entity.emit('starteventforAnimation');
});
}
});
</script>
</head>
<body>
<a-scene>
<a-entity class="rota" id="duck1" color="#FF0000" scale="0.1 0.1 .1" position="2 0 -7" animation="property: rotation;from: ; to:0 -360 0; loop:true; easing:linear; dur:30000; pauseEvents: mouseenter; resumeEvents: starteventforAnimation " animation__mouseenter="property: rotation;from: ; to:0 360 0; easing:linear; dur:4000; startEvents: mouseenter ;pauseEvents: starteventforAnimation; resumeEvents: mouseenter" eventhandling>
<a-box class="rota" color="#FF0000" gltf-model="spaceship.glb" position="20 0 -10" scale="2 3 3" collison-check="el: #otherduck; radius: 0.15; other-radius: 0.15;"> </a-box>
</a-entity>
<a-camera position="0 1.2 1.3"><a-cursor objects=".rota" ></a-cursor></a-camera> <
</a-scene>
</body>
</html>
You're looking for something called "world position".
The sphere is always at the same position regarding the box. The rotation of the box causes the sphere to move because the entire frame of reference is rotating. Its local position stays the same, but its world position is changing.
One way of getting the world position would be:
// local position
this.el.getAttribute("position");
// grab the reference to the underlaying object
const mesh = this.el.getObject3D("mesh");
// or const mesh = this.el.object3D;
// create a vector where the position will be copied to
const worldpos = new THREE.Vector3();
// get the world position - it's in the worldpos vector
mesh.getWorldPosition(worldpos);
Like i did here:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("foo", {
init: function() {
// i'll keep the local position here
this.localpos = new THREE.Vector3();
// I'll keep the world position here
this.worldpos = new THREE.Vector3();
// this is the reference to the <p> element
this.textEl = document.querySelector("p")
},
// utility function
posToString: function(pos) {
return pos.x.toFixed(2) + " " + pos.y.toFixed(2) + " " + pos.z.toFixed(2);
},
// called on each frame
tick: function() {
// getAttribute("position") has the local position
// this.el.object3D.position has the local position
// this.el.getObject3D("mesh").position has the local position
this.localpos.copy(this.el.getAttribute("position"))
this.el.getObject3D("mesh").getWorldPosition(this.worldpos)
// compose the displayed message
let msg = "";
msg += "Sphere local position:" + this.posToString(this.localpos)
msg += "<br>"
msg += "Sphere world position:" + this.posToString(this.worldpos)
this.textEl.innerHTML = msg
}
})
</script>
<p style="position: fixed; z-index: 999;"></p>
<a-scene>
<a-box position="0 1 -4" color="blue"
animation="property: rotation;from: ; to:0 -360 0; loop:true; easing:linear; dur:3000">
<a-sphere position="2 0 0" color="green" radius="0.25" foo></a-sphere>
</a-box>
</a-scene>

Find attached child objects of a-frame/ a-marker

I have a small A-frame based program to render text on a marker. When user click on the marker, I would like to scale the text, so it will be more visible. I can capture the click-event on the marker but how can I get it's child entities to scale?
My code can be found in https://codepen.io/asatrash/pen/rNLMgpa
<!DOCTYPE html>
<html>
<head>
<script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
<script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script>
</head>
<body>
<script>
//Multi Markers WebAR-AR.js and Aframe - Playing the Archive - Connected Environment CASA-UCL
//Global Variable
var markersURLArray=[];
var markersNameArray=[];
var allow_clicks = false;
AFRAME.registerComponent('markers_start',{
init:function(){
console.log('Add markers to the scene');
var sceneEl = document.querySelector('a-scene');
//list of the markers
for(var i=1; i<6; i++)`enter code here`
{
var url="https://raw.githubusercontent.com/asatrash/ARInWeb/main/resources/markers/pattern-Individual_Blocks-3.patt";
markersURLArray.push(url);
markersNameArray.push('Marker_'+i);
//console.log(url);
}
for(var k=0; k<5; k++)
{
var markerEl = document.createElement('a-marker');
markerEl.setAttribute('type','pattern');
markerEl.setAttribute('url',markersURLArray[k]);
markerEl.setAttribute('id',markersNameArray[k]);
markerEl.setAttribute('registerevents','');
sceneEl.appendChild(markerEl);
var boxEl = document.createElement('a-box');
boxEl.setAttribute('id','box');
boxEl.setAttribute('depth','0.001');
boxEl.setAttribute('width','6');
boxEl.setAttribute('height','4');
boxEl.setAttribute('opacity','0.25');
boxEl.setAttribute('position','0 -2 -4');
boxEl.setAttribute('rotation','0 0 0');
boxEl.setAttribute('material', {color: '#000000'});
markerEl.appendChild(boxEl);
var textEl = document.createElement('a-text');
textEl.setAttribute('id','text');
textEl.setAttribute('text',{color: '#ffff00', align: 'left', value:'This is a very log text /b \n line which might or might not wrap', width: '2' });
//textEl.setAttribute('position', '-1.4 1.5 0');
textEl.setAttribute('position', '-2.5 1.5 0')
textEl.setAttribute('scale', '2 2 0');
boxEl.appendChild(textEl);
}
}
});
//Detect marker found and lost
AFRAME.registerComponent('registerevents', {
init: function () {
const marker = this.el;
marker.addEventListener("markerFound", ()=> {
var markerId = marker.id;
console.log('Marker Found: ', markerId);
allow_clicks = true;
});
marker.addEventListener("markerLost",() =>{
var markerId = marker.id;
console.log('Marker Lost: ', markerId);
allow_clicks = false;
});
marker.addEventListener('click', (evt) => {
if (allow_clicks){
console.log("CLICKED!!!" + marker.id);
//I want to scale the box and the text attached to the marker jere
}
});
},
});
</script>
<a-scene markers_start vr-mode-ui="enabled: false" color-space="sRGB" renderer="gammaOutput: true"
embedded arjs='debugUIEnabled: false; sourceType: webcam; patternRatio: 0.85; trackingMethod: best;'>
<a-entity id='userCamera' camera look-controls >
<a-cursor> </a-cursor>
</a-entity>
</a-scene>
</body>
</html>
Did bit more testing and found out the following.
The 'click' event doesn't work as I expected on the marker. It is registered to the whole scene and it get invoked irrespective of the place I click. Which is not what I want. I am looking at using intersection Points to get it working.
However the getting access to the child can be easily done if you put unique ids. We can use the normal document.getElementById("boxelementId"); and change the attributes using the setAttribute method.
Ex: To change the text
var markerTextEl = document.getElementById(markerTextxId);
markerTextEl.setAttribute("value", "Current Temperature is: " + data);
I used above inside the 'markerFound' event to show different/current value when user point to a particular marker.

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 make an info window clickable?

I have the below code, based on one of the API samples. A click on the map creates a marker. A click on the marker opens up an info window. Now I want a click on the info window to do something. E.g. a click anywhere might close it, as opposed to the little cross in the corner. Or a click on it might open a new URL. Etc.
Unfortunately it seems there is no "click" event for info windows.
The closest I've got is shown as a commented out line below: I wrap my info window content in a div, and give that an onClick. This works, but there is a big border around it. I really want to be able to click anywhere in the info window box.
Is there a way?
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Click Test</title>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script>
google.maps.visualRefresh = true; //New look visuals.
function initialize() {
var useragent = navigator.userAgent;
var mapdiv = document.getElementById("map-canvas");
if (useragent.indexOf('iPhone') != -1 || useragent.indexOf('Android') != -1 ) {
mapdiv.style.width = '100%';
mapdiv.style.height = '100%';
} else {
mapdiv.style.width = '400px';
mapdiv.style.height = '600px';
}
var mapOptions = {
zoom:3,
center: new google.maps.LatLng(37.09024, -95.712891),
mapTypeId: google.maps.MapTypeId.TERRAIN
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
google.maps.event.addListener(map, 'click', function(event) {
placeMarker(event.latLng);
});
function placeMarker(location) {
var marker = new google.maps.Marker({
position: location,
map: map
});
var infowindow = new google.maps.InfoWindow({
//content: "<div onClick='test1()'>(lat,lng):<br/>"+location.lat()+","+location.lng()+"</div>"
content: "(lat,lng):<br/>"+location.lat()+","+location.lng()
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(marker.get('map'), marker);
infowindow.addListener('click',test1); //Does nothing
});
}
}
google.maps.event.addDomListener(window, 'load', initialize);
function test1(){alert("test1");}
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
UPDATE:
This image shows the problem when I use a clickable div inside the content (background set to red to show the extent of the region I can make clickable, and also that I can style; if I set a negative margin I just get scrollbars, not a bigger region!). It is the whole white area I want to be clickable, not just that red rectangle.
I decided to use InfoBox found in the Google Maps Utility Library. So in the header add a link to the library. Then replace the new google.maps.InfoWindow() line with this one:
var infowindow = new InfoBox({
closeBoxURL:"",
content: '<div onClick="test1();return false;" style="background:white;opacity:0.8;padding:8px">(lat,lng):<br/>'+
location.lat()+","+location.lng()+"</div>"
});
By setting closeBoxUrl to a blank string I get no close option. I added a large padding just so you can see that clicking right to the edge does indeed work.
You can also do it this way. I also use the boxClass option so the formatting is done in CSS:
var infoContent=document.createElement('div');
infoContent.innerHTML="(lat,lng):<br/>"+location.lat()+","+location.lng();
infoContent.onclick=test1;
var infowindow = new InfoBox({
closeBoxURL:"",
boxClass:"marker_popup",
content: infoContent,
});
(Aside, if doing it this way, on just some browsers it creates a marker below the InfoBox! Simplest fix is to change test1 so it looks like: function test1(event){alert("test1");event.preventDefault();return false;} )
P.S. I chose InfoBox over InfoBubble, as the latter has no documentation, and it had no obvious advantages to compensate for that major flaw! InfoBox has documentation and a reference. (links are for version 1.1.9)

Resources