Aframe fire raycaster on drop event - aframe

I'm trying to drop an object into a 3D scene from a React UI.
I can capture the drop data/ position etc, I'm now trying to use this in a RayCaster to determine what 3D entity has been dropped onto.
const handleDragStop = (e) => {
console.log(e)
const scene = document.getElementById('scene');
const raycaster:any = scene.getAttribute("raycaster");
const camera = AFRAME.scenes[0].camera;
console.log(raycaster)
console.log(AFRAME.scenes[0])
const mouse = new window.THREE.Vector2();
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
console.log(intersects)
};
The Aframe version of RayCaster does not seem to to have the raycaster.setFromCamera() function, it's not recognised as a function.
How do I fire a raycaster from a drop event?
EDIT
I can get it working with a new Raycaster
const raycaster = new window.THREE.Raycaster();
The new question is, how do I get this raycaster to use the same objects as the existing one?
Thanks

To access the THREE raycaster you have to do the following on an entity with the raycaster component:
entityEl.components.raycaster.raycaster.setFromCamera(..);
Make sure you are in the latest A-Frame release

Related

How to use a Personalized Map Style with Here API

I would like to customize the map style and found the Map Style Editor.
Therefore, I would like to build on the normal.day.grey map scheme and just tweak a few styles. The Map Style Editor provides the normal.day scheme as yaml.
Is there a possibility to access the normal.day.grey scheme as yaml so I can load it into the editor and start from there?
What would I do with the result? As far as I understand from the docs there is a predefined set of schemes and styles which can be combinated together. How would I use my custom style in there?
Please distinguish between vector map tiles and raster
In the documentation is described about raster Map Tile API - this is prerendered images (with some style) of map. You can't combine them in map view. Except only if some map image tile are partially transparent e.g. 'truck only' or 'traffic flow'. Then you can add two or more layers on the map.
Vector tiles (https://developer.here.com/documentation/vector-tiles-api/dev_guide/index.html):
Yes you can customize the vector map style utilize the Map Style Editor
vector style 'normal.day.grey' doesn't exist but you can start with this example on https://demo.support.here.com/examples/v3.1/changing_the_map_style - select there 'Reduced day' and download it.
In this example you can find dark style: https://jsfiddle.net/zmeatyxv/1/
Also you can mix a raster tiles and vector tiles adding layers in JS API:
http://jsfiddle.net/uycv9m8x/ - terrain map at the bottom and vector at the top, see code below:
/**
* The function add the "change" event listener to the map's style
* and modifies colors of the map features within that listener.
* #param {H.Map} map A HERE Map instance within the application
*/
function changeFeatureStyle(map){
// get the vector provider from the vector layer
var provider = map.getLayers().get(1).getProvider(); //get provider from vector layer
// get the style object for the vector layer
var vStyle = provider.getStyle();
var changeListener = (evt) => {
if (vStyle.getState() === H.map.Style.State.READY) {
vStyle.removeEventListener('change', changeListener);
// query the sub-section of the style configuration
// the call removes the subsection from the original configuration
var vConfig = vStyle.extractConfig([
'earth',
'landuse.wood',
'landuse.forest',
'landuse.park',
'landuse.builtup',
'landuse.national_park',
'landuse.nature_reserve',
'landuse.green-areas',
'landuse.other',
'water.small_water',
'water.deep_water',
'water.river'
]);
}
};
vStyle.addEventListener('change', changeListener);
}
/**
* Boilerplate map initialization code starts below:
*/
//Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map
var map = new H.Map(document.getElementById('map'),
defaultLayers.raster.terrain.xbase/*set terain xbase as base layer*/, {
center: {lat: 47.60586, lng: 14.27161},
zoom: 9,
pixelRatio: window.devicePixelRatio || 1
});
map.addLayer(defaultLayers.vector.normal.map);//add vector layer on the top
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Create the default UI components
var ui = H.ui.UI.createDefault(map, defaultLayers);
// Now use the map as required...
changeFeatureStyle(map);
http://jsfiddle.net/80jtLv16/1/ - raster mobile labels at the bottom and vector tile at the top, see code below:
function setStyle(map){
// get the vector provider from the vector layer
var provider = map.getLayers().get(1).getProvider(); //get provider from vector layer
// Create the style object from the YAML configuration.
// First argument is the style path and the second is the base URL to use for
// resolving relative URLs in the style like textures, fonts.
// all referenced resources relative to the base path https://js.api.here.com/v3/3.1/styles/omv.
var style = new H.map.Style('https://heremaps.github.io/maps-api-for-javascript-examples/change-style-at-load/data/dark.yaml',
'https://js.api.here.com/v3/3.1/styles/omv/');
// set the style on the existing layer
provider.setStyle(style);
}
/**
* The function add the "change" event listener to the map's style
* and modifies colors of the map features within that listener.
* #param {H.Map} map A HERE Map instance within the application
*/
function changeFeatureStyle(map){
// get the vector provider from the vector layer
var provider = map.getLayers().get(1).getProvider(); //get provider from vector layer
// get the style object for the vector layer
var vStyle = provider.getStyle();
var changeListener = (evt) => {
if (vStyle.getState() === H.map.Style.State.READY) {
vStyle.removeEventListener('change', changeListener);
// query the sub-section of the style configuration
// the call removes the subsection from the original configuration
var vConfig = vStyle.extractConfig([
'earth',
'continent_label',
'road_labels',
'places',
'buildings.address-labels',
'roads.shields',
//'landuse.wood',
//'landuse.forest',
//'landuse.park',
'landuse.builtup',
'landuse.national_park',
'landuse.nature_reserve',
'landuse.green-areas',
'landuse.other',
'water.small_water',
'water.deep_water',
'water.river'
]);
}
};
vStyle.addEventListener('change', changeListener);
}
/**
* Boilerplate map initialization code starts below:
*/
//Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
var mTileServ = platform.getMapTileService({'type': 'base'});
console.log("mTileServ:", mTileServ);
var mobileLayer = mTileServ.createTileLayer('labeltile', 'normal.day.mobile', 512, 'png8', {lg: 'eng', lg2: 'eng'});
//Step 2: initialize a map
var map = new H.Map(document.getElementById('map'),
mobileLayer/*set mobile layer as base layer*/, {
center: {lat: 47.60586, lng: 14.27161},
zoom: 9,
pixelRatio: window.devicePixelRatio || 1
});
map.addLayer(defaultLayers.vector.normal.map);//add vector layer on the top
map.getLayers().add(mobileLayer); //set base layer on top - it depends what you want to achieve
//map.getLayers().add(defaultLayers.vector.normal.map); //here example how to reshuffle the layers
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Create the default UI components
var ui = H.ui.UI.createDefault(map, defaultLayers);
// Now use the map as required...
setStyle(map);
changeFeatureStyle(map);

Include three.js into wordpress theme

So basically what I am trying to achieve is simple:
I want to use three.js with my current custom wordpress theme.
Unfortunately I can't seem to find any information on how to do so.
I enqueue all my scripts via functions.php, I guess that is the default approach.
Since three.js has to be loaded with "type=module" I can not get it to work properly, and it seems as if it has to be done in another way. Should the import statements take place in my themes header? I always think that it is bad practice... And how can I then write my three.js code into an external js file?
Can someone please help me out?
Since three.js has to be loaded with "type=module"
That is actually not true. three.js provides an ESM (three.module.js) and two UMD builds (three.js and three.min.js). Including the minified UMD build in your wordpress theme should solve the issue. The following lives example uses this build file:
let camera, scene, renderer;
let mesh;
init();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
const material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );
}
function animation( time ) {
mesh.rotation.x = time / 2000;
mesh.rotation.y = time / 1000;
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/build/three.min.js"></script>

Aframe Image with Depth one side only

I want to show an image with a frame. Using <a-image> gives me a plane with the image.
<a-box src="path/to/img.jpg> however gives me the image but it's giving me the image 6 times. Is it possible to get the box with am image at the front and any color at all other sides?
I don't know if you can do this with Aframe (I don't think so), but you can do it with Threejs, by making an array of materials that contain a material for each box face, and applying that to the box mesh.
const loadManager = new THREE.LoadingManager();
const loader = new THREE.TextureLoader(loadManager);
const materials = [
new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-1.jpg')}),
new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-2.jpg')}),
new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-3.jpg')}),
new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-4.jpg')}),
new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-5.jpg')}),
new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-6.jpg')}),
];
loadManager.onLoad = () => {
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);
cubes.push(cube); // add to our list of cubes to rotate
};
you can place this code inside of a custom component that is attached the cube geometry.
Here is the tutorial that the above code was taken from, on threejsfundamentals.

Aframe mesh rotation and animation

I am trying rotation for mesh in an aframe gltf model but its seems to be not working. Is it possible to rotate a mesh of gltf model added on runtime in the scene? I am getting mesh where pivot is set but unable to apply rotation to it.
Issue: I have a door model with two meshes. Left door and right door. I want to rotate door 180 degree when user clicks on door mesh. I got the click event on entire 3d object as of now and checking which mesh is clicked; checking its parent and trying to rotate the left door but not working. Any idea what am i missing.
so
object.parent
returns me parent object type which I am trying to rotate. Is it the right way?
Here is what I got so far.
const newElement = document.createElement('a-entity')
// The raycaster gives a location of the touch in the scene
const touchPoint = event.detail.intersection.point
newElement.setAttribute('position', touchPoint)
//const randomYRotation = Math.random() * 360
//newElement.setAttribute('rotation', '0 ' + randomYRotation + ' 0')
newElement.setAttribute('visible', 'false')
newElement.setAttribute('scale', '4 4 4')
newElement.setAttribute('gltf-model', '#animatedModel')
this.el.sceneEl.appendChild(newElement)
newElement.addEventListener('model-loaded', () => {
// Once the model is loaded, we are ready to show it popping in using an animation
newElement.setAttribute('visible', 'true')
newElement.setAttribute('id','model')
newElement.setAttribute('class','cantap')
newElement.setAttribute('hold-drag','')
newElement.setAttribute('two-finger-spin','')
newElement.setAttribute('pinch-scale','');
/* newElement.setAttribute('animation', {
property: 'scale',
to: '4 4 4',
easing: 'easeOutElastic',
dur: 800,
}) */
newElement.addEventListener('click', event => {
const animationList = ["Action", "Action.001"];
/* newElement.setAttribute('animation-mixer', {
clip: animationList[0],
loop: 'once',
})
newElement.addEventListener('animation-loop',function() {
newElement.setAttribute('animation-mixer', {
timeScale : 0
})
}); */
var object = event.detail.intersection.object;
document.getElementById("btn").innerHTML = object.parent;
/* object.setAttribute('animation', {
property: 'rotation',
to: '0 180 0',
loop: true,
dur: 6000,
dir: 'once'
});*/
object.parent.setAttribute('rotation', {x: 0, y: 180, z: 0});
/* object.traverse((node) =>{
console.log(node.name);
document.getElementById("btn").innerHTML = ;
}); */
console.log(this.el.getObject3D('mesh').name);
// name of object directly clicked
console.log(object.name);
// name of object's parent
console.log(object.parent.name);
// name of object and its children
});
})
The trick to doing anything to parts of a gltf model is to traverse the gltf and isolate the object inside that you want to manipulate.
You can do this by writing a component, attached to the gltf entity, that gets the underlying threejs object, and traverses all the objects within the gltf group, and then you can select an object by its name.
You do this inside of a "model-loaded" event listener, like this
el.addEventListener("model-loaded", e =>{
let tree3D = el.getObject3D('mesh');
if (!tree3D){return;}
console.log('tree3D', tree3D);
tree3D.traverse(function(node){
if (node.isMesh){
console.log(node);
self.tree = node;
}
});
This selects one of the models, assigns it to a variable, which can be later used, to rotate the model (or do whatever you like with it).
tick: function(){
if(this.tree){
this.tree.rotateY(0.01);
}
}
here is the glitch

flex: Drag and drop- object centering

In a drag+drop situation using Flex, I am trying to get the object center aligned to the point of drop- somehow, irrespective of the adjustments to height and width, it is always positioning drop point to left top.
here is the code..
imageX = SkinnableContainer(event.currentTarget).mouseX;
imageY = SkinnableContainer(event.currentTarget).mouseY;
// Error checks if imageX/imageY dont satisfy certain conditions- move to a default position
// img.width and img.height are both defined and traced to be 10- idea to center image to drop point
Image(event.dragInitiator).x = imageX-(img.width)/2;
Image(event.dragInitiator).y = imageY-(img.height)/2
The last 2 lines don't seem to have any effect. Any ideas why-must be something straightforward, that I am missing...
You can use the following snippet:
private function on_drag_start(event:MouseEvent):void
{
var drag_source:DragSource = new DragSource();
var drag_initiator:UIComponent = event.currentTarget as UIComponent;
var thumbnail:Image = new Image();
// Thumbnail initialization code goes here
var offset:Point = this.localToGlobal(new Point(0, 0));
offset.x -= event.stageX;
offset.y -= event.stageY;
DragManager.doDrag(drag_initiator, drag_source, event, thumbnail, offset.x + thumbnail.width / 2, offset.y + thumbnail.height / 2, 1.0);
}
Here is one important detail. The snippet uses stage coordinate system.
If you use event.localX and event.localY, this approach will fail in some cases. For example, you click-and-drag a movie clip. If you use localX and localY instead of stage coordinates, localX and localY will define coordinates in currently clicked part of the movie clip, not in the whole movie clip.
Use the xOffset and yOffset properties in the doDrag method of DragManager.
Look here for an example.

Resources