I'm using a component to set up reflections on a glb model using an equirectangular texture in AFRAME.
The code is the following :
HTML
<html>
<head>
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-orbit-controls#1.3.0/dist/aframe-orbit-controls.min.js"></script>
<script src="components.js"></script>
</head>
<body>
<a-scene
light="defaultLightsEnabled: true"
renderer="antialias: true; colorManagement: true;">
<a-assets>
<img id="sky" src="env.jpg">
<a-asset-item id="model" src="./helmet.glb"></a-asset-item>
</a-assets>
<a-entity
id="camera"
camera="near: 0.1; far: 1000"
orbit-controls="
target: 0 0 0;
zoomSpeed: 3;
minDistance: 0;
maxDistance: 100;
initialPosition: 5 2 5;
autoRotate: true;
autoRotateSpeed: 0.02;
enablePan: true">
</a-entity>
<a-entity gltf-model="#model" envmap></a-entity>
<a-sky src="#sky"></a-sky>
</a-scene>
</body>
</html>
JS
AFRAME.registerComponent("envmap", {
init: function() {
let mesh = this.el.getObject3D("mesh");
if (mesh) {
loadEnvMap(scene.renderer, this.el);
} else {
this.el.addEventListener("model-loaded", () => {
loadEnvMap(this.el.sceneEl.renderer, this.el);
});
}
}
});
function loadEnvMap(renderer, entity) {
let targetCube = new THREE.WebGLCubeRenderTarget(1024, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter });
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
renderer.outputEncoding = THREE.sRGBEncoding;
let mesh = entity.getObject3D("mesh");
let texture = new THREE.TextureLoader().load(
"./env.jpg",
function() {
let cubeTex = targetCube.fromEquirectangularTexture(renderer, texture);
mesh.traverse(function(el) {
if (el.material) {
el.material.envMap = cubeTex.texture;
el.material.envMap.intensity = 1;
el.material.needsUpdate = true;
}
});
});
}
The texture I use for the environment map is this one.
And the glb model is this one : https://sketchfab.com/3d-models/damaged-helmet-source-glb-fc8449ca5f3f490d83ba6a7ae087ff55
I get the following results: https://imgur.com/IDKPEyI
As you can see, the rendering on the side of the helmet is very pixelated and I don't really know why. The env.jpg file I have has a resolution of 4096x2048.
Any idea on how I could fix this ?
Related
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;
})
}
});
}
});
So I have created a heat map in Fusion Tables, obtained a API key and copied the code into the link in Google Earth Network Link add. I did not get the resulting heat map to populate in Google Earth. I've set the sharing to Public and Unlisted but neither bears any fruit. Can anyone tell me what steps I am missing to get the heat map to populate in Google Earth?
Here is my obtained code (XXX'd out my API key)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport"></meta>
<title>Copy of Heat Map - Google Fusion Tables</title>
<style type="text/css">
html, body, #googft-mapCanvas {
height: 300px;
margin: 0;
padding: 0;
width: 500px;
}
</style>
<script type="text/javascript" src="https://maps.google.com/maps/api/js?v=3&libraries=visualization"></script>
<script type="text/javascript">
if (window.location.protocol == "file:") {
alert('This script only works when loaded from a web server,' +
' not from a file on your computer.');
}
function ftOnLoadClientApi() {
gapi.client.setApiKey('XxxXxxXxxxXx');
}
</script>
<script type="text/javascript" src="https://apis.google.com/js/client.js?onload=ftOnLoadClientApi">
</script>
<script type="text/javascript">
var map;
function loadApi() {
gapi.client.load('fusiontables', 'v1', initialize);
}
function initialize() {
var isMobile = (navigator.userAgent.toLowerCase().indexOf('android') > -1) ||
(navigator.userAgent.match(/(iPod|iPhone|iPad|BlackBerry|Windows Phone|iemobile)/));
if (isMobile) {
var viewport = document.querySelector("meta[name=viewport]");
viewport.setAttribute('content', 'initial-scale=1.0, user-scalable=no');
}
var mapDiv = document.getElementById('googft-mapCanvas');
mapDiv.style.width = isMobile ? '100%' : '500px';
mapDiv.style.height = isMobile ? '100%' : '300px';
map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(28.070831120253633, -82.434892289917),
zoom: 11,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var query = 'select col2, col1 from 1-6pqmYvsmKGwt9IZcpIxFUmyYwmumoMAtMrE8pMe limit 1000';
var request = gapi.client.fusiontables.query.sqlGet({ sql: query });
request.execute(function(response) {
onDataFetched(response);
});
}
function onDataFetched(response) {
if (response.error) {
alert('Unable to fetch data. ' + response.error.message +
' (' + response.error.code + ')');
} else {
drawHeatmap(extractLocations(response.rows));
}
}
function extractLocations(rows) {
var locations = [];
for (var i = 0; i < rows.length; ++i) {
var row = rows[i];
if (row[0]) {
var lat = row[0];
var lng = row[1];
if (lat && lng && !isNaN(lat) && !isNaN(lng)) {
var latLng = new google.maps.LatLng(lat, lng);
locations.push(latLng);
}
}
}
return locations;
}
function drawHeatmap(locations) {
var heatmap = new google.maps.visualization.HeatmapLayer({
dissipating: true,
gradient: [
'rgba(102,255,0,0)',
'rgba(147,255,0,1)',
'rgba(193,255,0,1)',
'rgba(238,255,0,1)',
'rgba(244,227,0,1)',
'rgba(244,227,0,1)',
'rgba(249,198,0,1)',
'rgba(255,170,0,1)',
'rgba(255,113,0,1)',
'rgba(255,57,0,1)',
'rgba(255,0,0,1)'
],
opacity: 0.49,
radius: 20,
data: locations
});
heatmap.setMap(map);
}
google.maps.event.addDomListener(window, 'load', loadApi);
</script>
</head>
<body>
<div id="googft-mapCanvas"></div>
</body>
</html>
I'm having trouble displaying a map with OpenLayers v3 in JavaFX's WebView. Here's my code:
openLayersV3.html
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet"
href="https://openlayers.org/en/v3.19.1/css/ol.css" type="text/css">
<style>
.map {
height: 400px;
width: 400px;
}
</style>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList"></script>
<script src="https://openlayers.org/en/v3.19.1/build/ol.js"
type="text/javascript"></script>
<title>OpenLayers 3 example</title>
<script type="text/javascript">
function loadMap() {
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([10.0, 53.55]),
zoom: 10
})
});
}
</script>
</head>
<body onLoad="loadMap()">
<h2>My Map</h2>
<div id="map" class="map"></div>
</body>
</html>
And here's an excerpt of the loader:
OsmView.java
...
protected WebView webView = new WebView();
protected WebEngine webEngine = webView.getEngine();
public OsmView() {
final URL urlOsmMap = getClass().getResource("/some/package/name/openLayersV3.html");
webEngine.load(urlOsmMap.toExternalForm());
getChildren().add(webView);
}
...
When I load the html in a browser like IE or Firefox, it shows without any complications. But in the Java program, there is only a blank page with the h2 ("My Map"). But the JavaScript doesn't load. So, what am I doing wrong here?
Ok, actually I found a solution: the requestAnimationFrame is not found so you have to add the following lines before every other javascript stuff:
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback, element) {
window.setTimeout(callback, 1000 / 60);
};
})();
var requestAnimationFrame = window.requestAnimFrame;
I try to use three.js library in a-frame.
For example,
var helper = new THREE.MMDHelper(renderer);
I don't know how to get a renderer in a-frame.
a-scene has a renderer property.
=> https://aframe.io/docs/0.3.0/core/scene.html
but, it's undefined.
document.querySelector('a-scene').renderer; <= undefined
Please tell me how to get the rendere
You'll need to wait for the scene to initialize before a renderer is available:
sceneEl.addEventListener('render-target-loaded', function () {
// sceneEl.renderer is now set.
});
If you write your code within a component, you can be guaranteed everything is loaded:
AFRAME.registerComponent('do-something', {
init: function () {
console.log(this.el.renderer);
}
});
<script src="do-something-component.js"></script>
<a-scene do-something></a-scene>
thanks, Don McCurdy and ngokevin.
Incidentally, I looked for the camera property.
I can get the camera property by use 'camera-set-active'.
For your reference.
<html>
<head>
<title>a-frame sample</title>
<script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script>
</head>
<body>
<a-scene do-something>
</a-scene>
</body>
<script>
var sceneEl = document.querySelector('a-scene');
window.onload = function() {
console.log('window.onload');
console.log(document.querySelector('a-scene').renderer); // undefined
console.log(document.querySelector('a-scene').camera); // undefined
}
AFRAME.registerComponent('do-something', {
init: function() {
console.log('do-something init');
console.log(this.el.renderer); // OK
console.log(this.el.camera); // undefined
this.el.addEventListener('camera-set-active', this.cameraSetActive.bind(this));
},
cameraSetActive: function() {
console.log('do-something cameraSetActive');
console.log(this.el.renderer); // OK
console.log(this.el.camera); // OK
}
});
sceneEl.addEventListener('render-target-loaded', function() {
console.log('render-target-loaded');
console.log(document.querySelector('a-scene').renderer); // OK
console.log(document.querySelector('a-scene').camera); // undefined
});
sceneEl.addEventListener('camera-ready', function() {
console.log('camera-ready');
console.log(document.querySelector('a-scene').renderer); // OK
console.log(document.querySelector('a-scene').camera); // undefined
});
sceneEl.addEventListener('camera-set-active', function() {
console.log('camera-set-active');
console.log(document.querySelector('a-scene').renderer); // OK
console.log(document.querySelector('a-scene').camera); // OK
});
</script>
</html>
I want to enable drawing on Google Maps like (see this example)
When user finish with drawings he will click on save button to save his drawings in Database or KML file, anything :) .. I do not know how to the save part? Could anyone help me
Here, http://jsfiddle.net/X66L4/1/ try drawing some circles, click on SAVE, then edit the circles by switching to the hand cursor and SAVE again to see the changes.
I show an example to save circles' data, the main idea is to keep a global array for each drawing type (line, polygon, marker, circle), and use a listener on the drawing manager to detect each type being drawn (complete).
var circles = [];
google.maps.event.addDomListener(drawingManager, 'circlecomplete',
function(circle) {
circles.push(circle);
});
The reason to save the entire reference to the drawn object is to continue tracking changes. So you will need an array and listener for each type of drawing.
Then, when you want to save the data (you may wish to do so at every edit), iterate through the arrays and extract the minimum information to rebuild it (center, radius, path, latLng, and so on.)
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
html, body, #map_canvas { margin: 0; padding: 0; height: 100% }
</style>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false&libraries=drawing"></script>
<script type="text/javascript">
var myOptions = {
center: new google.maps.LatLng(-25,177.5),
zoom: 3,
mapTypeId: google.maps.MapTypeId.SATELLITE
};
var map;
function initialize() {
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.CIRCLE,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [google.maps.drawing.OverlayType.CIRCLE]
},
circleOptions: {
editable: true
}
});
drawingManager.setMap(map);
var circles = [];
google.maps.event.addDomListener(drawingManager, 'circlecomplete', function(circle) {
circles.push(circle);
});
google.maps.event.addDomListener(savebutton, 'click', function() {
document.getElementById("savedata").value = "";
for (var i = 0; i < circles.length; i++) {
var circleCenter = circles[i].getCenter();
var circleRadius = circles[i].getRadius();
document.getElementById("savedata").value += "circle((";
document.getElementById("savedata").value +=
circleCenter.lat().toFixed(3) + "," + circleCenter.lng().toFixed(3);
document.getElementById("savedata").value += "), ";
document.getElementById("savedata").value += circleRadius.toFixed(3) + ")\n";
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<button id="savebutton">SAVE</button>
<textarea id="savedata" rows="8" cols="40"></textarea>
<div id="map_canvas"></div>
</body>
</html>
In my experience, it's easier to use map's dataLayer then the drawing manager.
Try out this fiddle.
FiddleLink
Showing the controls:
map.data.setControls(['Polygon']);
map.data.setStyle({
editable: true,
draggable: true
});
in this function you can see the Create, Read (localStorage) and Remove (not in that order):
function loadPolygons(map) {
var data = JSON.parse(localStorage.getItem('geoData'));
map.data.forEach(function (f) {
map.data.remove(f);
});
console.log(data);
map.data.addGeoJson(data)
}