Alpha Animation in Aframe for a-object-model - aframe

I have one 3d object with its obj and mtl file which is displayed using in Aframe. I want to apply animation on it which change its Alpha value gradually for Fade out effect.
I went through AFrame doc. but couldn't find anything related to 3d object alpha animation.

The built-in material component primarily works with primitives, so material="opacity: 0.5" and similarly opacity="0.5" will not work here. You'll need to modify the THREE.js materials created by your model, using a custom component. Example:
AFRAME.registerComponent('model-opacity', {
schema: {default: 1.0},
init: function () {
this.el.addEventListener('model-loaded', this.update.bind(this));
},
update: function () {
var mesh = this.el.getObject3D('mesh');
var data = this.data;
if (!mesh) { return; }
mesh.traverse(function (node) {
if (node.isMesh) {
node.material.opacity = data;
node.material.transparent = data < 1.0;
node.material.needsUpdate = true;
}
});
}
});
You can then use, and animate, the component like this:
<a-entity obj-model="obj: model.obj; mtl: model.mtl;" model-opacity="1">
<a-animation attribute="model-opacity"
dur="10000"
from="1"
to="0"
repeat="indefinite"></a-animation>
</a-entity>
For more on how this works, see the docs on THREE.Material and writing a component.

For me, the material was all white, so I had to change the component by Don to support a number:
AFRAME.registerComponent("model-opacity", {
schema: {
opacity: { type: "number", default: 0.5 }
},
init: function() {
this.el.addEventListener("model-loaded", this.update.bind(this));
},
update: function() {
var mesh = this.el.getObject3D("mesh");
var data = this.data;
if (!mesh) {
return;
}
mesh.traverse(function(node) {
if (node.isMesh) {
console.log(node);
node.material.opacity = data.opacity;
node.material.transparent = data.opacity < 1.0;
node.material.needsUpdate = true;
}
});
}
});

I had to modify the above examples as mine had multiple materials, so I had to use forEach and adjust for all of them:
AFRAME.registerComponent("model-opacity", {
schema: {
opacity: { type: "number", default: 0.5 }
},
init: function() {
this.el.addEventListener("model-loaded", this.update.bind(this));
},
update: function() {
var mesh = this.el.getObject3D("mesh");
var data = this.data;
if (!mesh) {
return;
}
mesh.traverse(function(node) {
if (node.isMesh) {
console.log(node);
node.material.forEach( (mtl) =>{
mtl.opacity = data.opacity;
mtl.transparent = data.opacity < 1.0;
mtl.needsUpdate = true;
})
}
});
}
});

Related

How to add point on polyline somewhere between two vertexes?

The goal is to have an editable polyline with customized markers at every vertex.
As I see Map API V3 does not allows that. So I made editable polyline like this:
let polyline = new self.google.maps.Polyline({
strokeColor: '#000000',
strokeOpacity: 0.3,
strokeWeight: 3,
editable: false,
path: path,
map: self.map
});
polyline.binder = new MVCArrayBinder(polyline.getPath());
let markers = [];
for (let i = 0; i < path.length; i++) {
let marker = this.getBasicMarker(path[i]);
marker.bindTo('position', polyline.binder, i.toString());
markers.push(marker);
}
getBasicMarker(position){
return new this.google.maps.Marker({
position: position,
map: this.map,
icon: {
url: "https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle.png",
size: new google.maps.Size(7, 7),
anchor: new google.maps.Point(3.5, 3.5)
},
draggable: true,
visible: false
});
}
function MVCArrayBinder(mvcArray) {
this.array_ = mvcArray;
}
MVCArrayBinder.prototype = new google.maps.MVCObject();
MVCArrayBinder.prototype.get = function (key) {
if (!isNaN(parseInt(key))) {
return this.array_.getAt(parseInt(key));
} else {
this.array_.get(key);
}
};
MVCArrayBinder.prototype.set = function (key, val) {
if (!isNaN(parseInt(key))) {
this.array_.setAt(parseInt(key), val);
} else {
this.array_.set(key, val);
}
};
Final polyline now looks like this: http://sandbox.rubera.ru/img/2019-05-28_17-04-25.jpg. I can drag any existing point. The polyline updating while dragging the point.
Now I have to add new vertex somewhere between two existing.
This is where I'm stuck.
May be there is another solution how to solve the task?
In case anybody face similar task here is a few modifications to my code posted abowe. That helped me finally solve the problem.
MVCArrayBinder.prototype = new google.maps.MVCObject();
MVCArrayBinder.prototype.get = function (key) {
if (!isNaN(parseInt(key))) {
return this.array_.getAt(parseInt(key));
} else {
this.array_.get(key);
}
};
MVCArrayBinder.prototype.set = function (key, val) {
if (!isNaN(parseInt(key))) {
this.array_.setAt(parseInt(key), val);
} else {
this.array_.set(key, val);
}
};
MVCArrayBinder.prototype.insertAt = function (key, val) {
this.array_.insertAt(key, val);
};
google.maps.event.addListener(this.activePoly, 'click', function (e) {
let path = $this.activePoly.getPath(),
inserted = false;
// find line segment
for (let i = 0; i < path.getLength() - 1, !inserted; i++) {
let tempPoly = new google.maps.Polyline({
path: [path.getAt(i), path.getAt(i + 1)]
});
if (google.maps.geometry.poly.isLocationOnEdge(e.latLng, tempPoly, 10e-2)) {
$this.activeRoute.binder.insertAt(i + 1, e.latLng);
inserted = true;
let marker = $this.getBasicMarker(e.latLng);
marker.setVisible(true);
// Add new marker to array
$this.activeRoute.markers.splice(i + 1, 0, marker);
// Have to rebind all markers
$this.activeRoute.markers.forEach((marker, index) => {
marker.bindTo('position', $this.activeRoute.binder, index.toString());
});
}
}
});
My activeRoute is an object with following structure:
{polyline: polyline, markers: markers, binder: polyline.binder}

A-FRAME Extend component

Consider this:
<a-entity id="player">
<a-entity id="camera" camera look-controls></a-entity>
<a-entity id="leftHand" oculus-touch-controls="hand: left"></a-entity>
<a-entity id="rightHand" oculus-touch-controls="hand: right"></a-entity>
</a-entity>
If I want to move my player through the scene, I would add wasd-controls to #player. Doing that, disregards the orientation of the head (camera): W always moves "north", wherever you are looking at. If I add wasd-controls to #camera, the head moves correctly but the controllers are left behind.
So I thought of creating a custom wasd-controls, but I am unable to extend that component. I have been successful copying and pasting all the code, but that is very nasty.
This did not work: AFrame extend component and override.
Any idea?
my-wasd-controls.js
var KEYCODE_TO_CODE = require('aframe/src/constants').keyboardevent.KEYCODE_TO_CODE;
var AFRAME = require('aframe');
var THREE = require('aframe/src/lib/three');
var utils = require('aframe/src/utils');
var bind = utils.bind;
var shouldCaptureKeyEvent = utils.shouldCaptureKeyEvent;
var CLAMP_VELOCITY = 0.00001;
var MAX_DELTA = 0.2;
var KEYS = [
'KeyW', 'KeyA', 'KeyS', 'KeyD',
'ArrowUp', 'ArrowLeft', 'ArrowRight', 'ArrowDown'
];
/**
* WASD component to control entities using WASD keys.
*/
module.exports.Component = AFRAME.registerComponent('my-wasd-controls', {
schema: {
acceleration: {default: 65},
adAxis: {default: 'x', oneOf: ['x', 'y', 'z']},
adEnabled: {default: true},
adInverted: {default: false},
easing: {default: 20},
enabled: {default: true},
fly: {default: false},
head: {type: 'selector'},
wsAxis: {default: 'z', oneOf: ['x', 'y', 'z']},
wsEnabled: {default: true},
wsInverted: {default: false}
},
init: function () {
// To keep track of the pressed keys.
this.keys = {};
this.position = {};
this.velocity = new THREE.Vector3();
// Bind methods and add event listeners.
this.onBlur = bind(this.onBlur, this);
this.onFocus = bind(this.onFocus, this);
this.onKeyDown = bind(this.onKeyDown, this);
this.onKeyUp = bind(this.onKeyUp, this);
this.onVisibilityChange = bind(this.onVisibilityChange, this);
this.attachVisibilityEventListeners();
},
tick: function (time, delta) {
var currentPosition;
var data = this.data;
var el = this.el;
var movementVector;
var position = this.position;
var velocity = this.velocity;
if (!velocity[data.adAxis] && !velocity[data.wsAxis] &&
isEmptyObject(this.keys)) { return; }
// Update velocity.
delta = delta / 1000;
this.updateVelocity(delta);
if (!velocity[data.adAxis] && !velocity[data.wsAxis]) { return; }
// Get movement vector and translate position.
currentPosition = el.getAttribute('position');
movementVector = this.getMovementVector(delta);
position.x = currentPosition.x + movementVector.x;
position.y = currentPosition.y + movementVector.y;
position.z = currentPosition.z + movementVector.z;
el.setAttribute('position', position);
},
remove: function () {
this.removeKeyEventListeners();
this.removeVisibilityEventListeners();
},
play: function () {
this.attachKeyEventListeners();
},
pause: function () {
this.keys = {};
this.removeKeyEventListeners();
},
updateVelocity: function (delta) {
var acceleration;
var adAxis;
var adSign;
var data = this.data;
var keys = this.keys;
var velocity = this.velocity;
var wsAxis;
var wsSign;
adAxis = data.adAxis;
wsAxis = data.wsAxis;
// If FPS too low, reset velocity.
if (delta > MAX_DELTA) {
velocity[adAxis] = 0;
velocity[wsAxis] = 0;
return;
}
// Decay velocity.
if (velocity[adAxis] !== 0) {
velocity[adAxis] -= velocity[adAxis] * data.easing * delta;
}
if (velocity[wsAxis] !== 0) {
velocity[wsAxis] -= velocity[wsAxis] * data.easing * delta;
}
// Clamp velocity easing.
if (Math.abs(velocity[adAxis]) < CLAMP_VELOCITY) { velocity[adAxis] = 0; }
if (Math.abs(velocity[wsAxis]) < CLAMP_VELOCITY) { velocity[wsAxis] = 0; }
if (!data.enabled) { return; }
// Update velocity using keys pressed.
acceleration = data.acceleration;
if (data.adEnabled) {
adSign = data.adInverted ? -1 : 1;
if (keys.KeyA || keys.ArrowLeft) { velocity[adAxis] -= adSign * acceleration * delta; }
if (keys.KeyD || keys.ArrowRight) { velocity[adAxis] += adSign * acceleration * delta; }
}
if (data.wsEnabled) {
wsSign = data.wsInverted ? -1 : 1;
if (keys.KeyW || keys.ArrowUp) { velocity[wsAxis] -= wsSign * acceleration * delta; }
if (keys.KeyS || keys.ArrowDown) { velocity[wsAxis] += wsSign * acceleration * delta; }
}
},
getMovementVector: (function () {
var directionVector = new THREE.Vector3(0, 0, 0);
var rotationEuler = new THREE.Euler(0, 0, 0, 'YXZ');
return function (delta) {
var rotation = (this.data.head || this.el).getAttribute('rotation');
var velocity = this.velocity;
var xRotation;
directionVector.copy(velocity);
directionVector.multiplyScalar(delta);
// Absolute.
if (!rotation) { return directionVector; }
xRotation = this.data.fly ? rotation.x : 0;
// Transform direction relative to heading.
rotationEuler.set(THREE.Math.degToRad(xRotation), THREE.Math.degToRad(rotation.y), 0);
directionVector.applyEuler(rotationEuler);
return directionVector;
};
})(),
attachVisibilityEventListeners: function () {
window.addEventListener('blur', this.onBlur);
window.addEventListener('focus', this.onFocus);
document.addEventListener('visibilitychange', this.onVisibilityChange);
},
removeVisibilityEventListeners: function () {
window.removeEventListener('blur', this.onBlur);
window.removeEventListener('focus', this.onFocus);
document.removeEventListener('visibilitychange', this.onVisibilityChange);
},
attachKeyEventListeners: function () {
window.addEventListener('keydown', this.onKeyDown);
window.addEventListener('keyup', this.onKeyUp);
},
removeKeyEventListeners: function () {
window.removeEventListener('keydown', this.onKeyDown);
window.removeEventListener('keyup', this.onKeyUp);
},
onBlur: function () {
this.pause();
},
onFocus: function () {
this.play();
},
onVisibilityChange: function () {
if (document.hidden) {
this.onBlur();
} else {
this.onFocus();
}
},
onKeyDown: function (event) {
var code;
if (!shouldCaptureKeyEvent(event)) { return; }
code = event.code || KEYCODE_TO_CODE[event.keyCode];
if (KEYS.indexOf(code) !== -1) { this.keys[code] = true; }
},
onKeyUp: function (event) {
var code;
code = event.code || KEYCODE_TO_CODE[event.keyCode];
delete this.keys[code];
}
});
function isEmptyObject (keys) {
var key;
for (key in keys) { return false; }
return true;
}
I would still recommend copy and pasting it. If you ever upgrade A-Frame later, and wasd-controls changes, your extensions will probably break.
Changing the prototype should work (e.g., AFRAME.components['wasd-controls'].Component.prototype.foo = () => {}). The prototype methods are writable.
Another alternative is to overwrite the method on the component instance. el.components['wasd-controls'].foo = () => {}.

How to implement dynamic reflections in A-Frame

I have an A-Frame scene consisting of a horizontal plane, some lights and a vertical plane on which a video is playing. I am trying to achieve reflections on the horizontal plane such that the vertical plane above it is reflected. I intend to set reduced opacity on the reflective surface. How can one do this in A-Frame?
I write a mirror component, here is my code:
<script>
AFRAME.registerComponent('mirror', {
schema:{
renderothermirror:{default:true},
},
init: function () {
var scene = this.el.sceneEl;
scene.addEventListener('render-target-loaded',this.OnRenderLoaded.bind(this));
},
OnRenderLoaded: function()
{
var mirrorObj = this.el.getOrCreateObject3D('mesh',THREE.Mesh);
var cameraEl = document.querySelector('a-entity[camera]')
if(!cameraEl)
{
cameraEl = document.querySelector('a-camera');
}
var camera = cameraEl.components.camera.camera;
var scene = this.el.sceneEl;
this.renderer = scene.renderer;
this.mirror = new THREE.Mirror( this.renderer, camera, { clipBias: 0.003, textureWidth: window.innerWidth, textureHeight: window.innerHeight, color: 0x777777 } );
mirrorObj.material =this.mirror.material;
mirrorObj.add(this.mirror);
},
tick: function () {
if(!this.data.renderothermirror)
{
this.mirror.render();
}
else
{
var mirrors = [];
var mirrorEls = document.querySelectorAll("a-entity[mirror]");
for(var i=0;i<mirrorEls.length;i++)
{
if(mirrorEls[i]!=this.el)
{
mirrors.push(mirrorEls[i].components.mirror.mirror);
}
}
if(mirrors.length === 0)
{
this.mirror.render();
}
for(var i = 0; i<mirrors.length;i++)
{
this.mirror.renderWithMirror(mirrors[i]);
}
}
}
});
</script>
You also need to use Mirror.js, you can find it here:Mirror.js
Just attach the mirror component to your planes. e.g. <a-plane mirror>.

How to show dynamically multiple popup in openlayers 3 map

Can anyone tell me how to show all popup of markers in openlayers 3 map. I searched many sites but couldn't get any answer please anyone know about this then help me
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.TileJSON({
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
crossOrigin: 'anonymous'
})
})
],
overlays: [overlay],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([0, 50]),
zoom: 2
})
});
var vectorSource = new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([16.37, 48.2])),
name: 'London'
}),
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([-0.13, 51.51])),
name: 'NY'
}),
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([30.69, 55.21])),
name: 'Paris'
})
]
});
var markers = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
image: new ol.style.Icon({
src: '//openlayers.org/en/v3.12.1/examples/data/icon.png',
anchor: [0.5, 1]
})
})
});
map.addLayer(markers);
function showpopup(){
// For showing popups on Map
var arrayData = [1];
showInfoOnMap(map,arrayData,1);
function showInfoOnMap(map, arrayData, flag) {
var flag = 'show';
var extent = map.getView().calculateExtent(map.getSize());
var id = 0;
var element = document.getElementById('popup');
var popup = new ol.Overlay({
element: element,
positioning: 'center'
});
map.addOverlay(popup);
if (arrayData != null && arrayData.length > 0) {
arrayData.forEach(function(vectorSource) {
/* logMessage('vectorSource >> ' + vectorSource); */
if (vectorSource != null && markers.getSource().getFeatures() != null && markers.getSource().getFeatures().length > 0) {
markers.getSource().forEachFeatureInExtent(extent, function(feature) {
/* logMessage('vectorSource feature >> ' + feature); */
console.log("vectorSource feature >> " + markers.getSource().getFeatures());
if (flag == 'show') {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
popup.setPosition(coord);
/* var prop;
var vyprop = ""; */
$(element).popover({
'position': 'center',
'placement': 'top',
'template':'<div class="popover"><div class="popover-content"></div></div>',
'html': true,
'content': function() {
var string = [];
var st = feature.U.name;
if (st != null && st.length > 0) {
var arrayLength = 1;
string = "<table>";
string += '<tr><td>' + st + "</table>";
}
return string;
}
});
$(element).popover('show');
} else {
$(element).popover('destroy');
}
});
}
});
}
};
}
I used this code in my file but it show only one popup on all markers please someone tell me how to show all markers popup simultaneously.
I'm not sure exactly what you're trying to show in your popups, but I would probably try this approach. This extends the ol.Overlay class, allowing you to get the map object and attach a listener which you can use to grab the feature that was clicked. Is this what you're trying to accomplish?
function PopupOverlay() {
var element = document.createElement('div');
$(element).popover({
template: '<div class="popover"><div class="popover-content"></div></div>',
placement: 'top',
position: 'center',
html: true
});
ol.Overlay.call(this, {
element: element
});
}
ol.inherits(PopupOverlay, ol.Overlay);
PopupOverlay.prototype.setMap = function (map) {
var self = this;
map.on('singleclick', function (e) {
map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
ol.Overlay.prototype.setPosition.call(self, feature.getGeometry().getCoordinates());
var el = self.getElement();
$(el).data('bs.popover').options.content = function () {
// EDIT THE POPOVER CONTENT
return feature.get('name');
};
$(el).popover('show');
});
});
ol.Overlay.prototype.setMap.call(this, map);
};
Check out this example
So after your comment, I see what you're trying to do now. I would say that you want to take the same basic approach, make a class that overrides ol.Overlay, but this time just loop through all the features, creating an overlay for each feature.
This Updated Example
function PopoverOverlay(feature, map) {
this.feature = feature;
var element = document.createElement('div');
$(element).popover({
template: '<div class="popover"><div class="popover-content"></div></div>',
placement: 'top',
position: 'center',
html: true
});
ol.Overlay.call(this, {
element: element,
map: map
});
};
ol.inherits(PopoverOverlay, ol.Overlay);
PopoverOverlay.prototype.togglePopover = function () {
ol.Overlay.prototype.setPosition.call(this, this.feature.getGeometry().getCoordinates());
var self = this;
var el = this.getElement();
$(el).data('bs.popover').options.content = function () {
// EDIT THE POPOVER CONTENT
return self.feature.get('name');
};
$(el).popover('toggle');
};
// create overlays for each feature
var overlays = (function createOverlays () {
var popupOverlays = [];
vectorSource.getFeatures().forEach(function (feature) {
var overlay = new PopoverOverlay(feature, map);
popupOverlays.push(overlay);
map.addOverlay(overlay);
});
return popupOverlays;
})();
// on click, toggle the popovers
map.on('singleclick', function () {
for(var i in overlays) {
overlays[i].togglePopover();
}
});
Now when you click anywhere on the map, it should call the togglePopover method and toggle the popover on the individual element.

Mapbox fitBounds, function bounds map but does not render tiles

Here, my javascript code to render markers in map and after that fitBounds of that map.
Map bounds are get fit according to geojson, but problem is that when there is any marker
on map and I try to fit bound, map tiles images are not get render.
In map it display only markers, no tile images.
var latLongCollection = [];
var map;
$(document).ready(function() {
map();
});
function map() {
map = L.mapbox.map('DivId', 'ProjectId');
GetData();
}
function GetData() {
$.ajax({
type: "GET",
url: someUrl,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: AjaxSuccess,
error: function () {
}
});
}
function AjaxSuccess(data) {
if (data == null || data.length == 0) {
return;
}
var geoJson = {
"type": "FeatureCollection",
"features": []
};
$.each(data, function (index, value) {
var latitude = parseFloat(value.Latitude),
longitude = parseFloat(value.Longitude);
if (latitude && longitude) {
var dataJson = {
type: "Feature",
geometry: { type: "Point", coordinates: [longitude, latitude] },
properties: {
someProp: value.SomeProperty,
}
};
geoJson.features.push(vehicleJson);
}
});
var markerLayer = L.mapbox.featureLayer(geoJson).addTo(map);
markerLayer.eachLayer(function (marker) {
var feature = marker.feature;
var featureProperty = feature.properties;
if (featureProperty.someProp) {
marker.setIcon(L.divIcon({
html: 'htmlstring',
iconSize: [35, 75]
}));
}
else {
marker.setIcon(L.divIcon({
html: 'anotherhtmlstring',
iconSize: [35, 75]
}));
}
latLongCollection.push(marker._latlng);
});
markerLayer.on('click', function (e) {
map.panTo(e.layer.getLatLng());
});
if (latLongCollection.length > 0) {
map.fitBounds(latLongCollection);
}
}
If anyone is still struggling with this, it appears to be a bug in Leaflet: https://github.com/Leaflet/Leaflet/issues/2021
It has been fixed in the latest version, but if you can't update you can work around the race condition by setting a timeout:
setTimeout(function () {
map.fitBounds(latLongCollection);
}, 0);
How did you try debug the problem? What's your network and js console saying?
Try to zoom out, maybe fitBounds zooms your map too much. I had this problem. The solution was using maxSize option in fitBounds, see leaflet docs.

Resources