I have the following assets:
<a-assets>
<a-asset-item id="model-obj" src="the-source..."></a-asset-item>
<a-asset-item id="model-mtl" src="another-source..."></a-asset-item>
</a-assets>
And after the scene is loaded I attached the following event listener which is never called and I don't know why (although the model loads and is shown in the scene):
document.querySelector('#model-obj').addEventListener('loaded', function() {
console.log('loaded');
});
According to the docs it should work (https://aframe.io/docs/0.5.0/core/asset-management-system.html#lt-a-asset-item-gt).
That's odd. This code works for me (but I'm using a glTF model):
<a-assets>
<a-asset-item id="duck" src="duck/duck.gltf"></a-asset-item>
</a-assets>
and
document.getElementById('duck').addEventListener('loaded', function() {
alert('ok')
})
A few pointers:
Is your script running after you've defined the element?
Does the network inspector show you an unsuccessful load (HTTP 404, 500, etc.)?
Any JS errors on your page in the console?
Related
I know that three.js can export an object to glb.
What I'm trying to do is to export a aframe plane to a glb file.
I thought aframe included three.js so I tried something like this:
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<title>Demo</title>
</head>
<body>
<a-scene
embedded
vr-mode-ui="enabled: false;"
background="color: #24CAFF;"
renderer="colorManagement: true;"
inspector=""
keyboard-shortcuts=""
screenshot=""
device-orientation-permission-ui=""
>
<a-plane
id="plane"
position="0 0 0"
rotation="0 0 0"
width="10"
height="10"
material="src: texturefile.jpg; repeat: 16 16">
></a-plane>
</a-scene>
<script>
var gltfExporter = new THREE.GLTFExporter();
gltfExporter.parse(document.querySelector('#plane').getObject3D('mesh').el, function (result) {
console.log(result);
};
</script>
</body>
</html>
I can't get the GLTF exporter from three JS.
I tried new GLTFExporter();
Just GLTFExporter() and THREE.GLTFExporter() without the new keyword. (to check if there is already an instance available)
none of these work?
So how can I export this plane to a GLB file?
kind regards
I thought aframe included three.js
a-frame uses a fork of three.js - super-three. It's a bit behind (to keep things stable), and it has some minor differences (utility functions, some loaders).
Another thing worth mentioning - the three.js build is limited to a number of core features - the rest is supposed to be included if you need them.
So - if you browse the threejs examples and see the one exporting gltf, you should check the source. A line like this:
import { GLTFExporter } from './jsm/exporters/GLTFExporter.js';
indicates, that the GLTFExporter is imported from elsewhere. jsm has modules, js has js objects.
So, in your case a simple script import:
<script src="https://threejs.org/examples/js/exporters/GLTFExporter.js"></script>
should be sufficient, to create the exporter and use it:
const exporter = new THREE.GLTFExporter();
exporter.parse(obj, callback, error, options)
You can check out a simple example of using it with a-frame here.
Using A-Frame creates a lot of clutter. Is their a way to access the code from another file? If not then what do you recommend for preventing cluster? My code is public # https://repl.it/#ColbzDaBoss1/A-Frame-Test
Thanks :)
Colby Bianco
Use components: https://aframe.io/docs/0.9.0/introduction/writing-a-component.html
<script src="my-component.js"></script>
<a-scene>
<a-entity my-component></a-entity>
</a-scene>
component file:
AFRAME.registerComponent('my-component', {
init: function () {
}
});
A-Frame Mixins go into the <a-assets> element, which must be defined before the scene is rendered. This makes sense for pre-loading/caching images, videos etc, but it seems there should be a way to dynamically create and use mixins.
Just adding the mixin to <a-assets> at runtime does not seem to work. The recommendation for adding image assets at runtime is to inline the image source and set it on the material directly. Is there some similar way of defining/altering a mixin at runtime? Or do I just need to set the relevant properties on all the objects to which the mixin is applied (taking care to also account for properties having been set by other mixins later in the mixin chain or directly on the object itself)
Edit: It looks like aframe-asset-on-demand-component is designed to do this for image/video assets. Unclear if works for mixins, but it also hasn't been updated in a year. Is this a (semi-)officially recommended solution?
Sorry if I've misunderstood your question but I seem to be able to add mixins to the assets tag at runtime. A basic version would mean writing a component as follows;
// add assets at run time
AFRAME.registerComponent('addasset', {
init: function () {
var assets = document.getElementsByTagName('a-assets')[0]
var mixin = document.createElement('a-mixin')
mixin.setAttribute('id', 'makeitred')
mixin.setAttribute('material', 'color: red')
assets.appendChild(mixin)
},
});
And then attach that component to the scene as follows;
<a-scene addasset>
<a-assets>
</a-assets>
<a-cylinder
mixin="makeitred"
position="0 0.5 -3">
</a-cylinder>
</a-scene>
Here is a working glitch
To demonstrate how that could be added once the scene is running here is a version of the same component with a setTimeout to demonstrate how the mixin could be added later on.
// add assets at run time, delayed
AFRAME.registerComponent('addasset', {
init: function () {
setTimeout(function(){
var assets = document.getElementsByTagName('a-assets')[0]
var mixin = document.createElement('a-mixin')
var cylinder = document.getElementsByTagName('a-cylinder')[0]
mixin.setAttribute('id', 'makeitred')
mixin.setAttribute('material', 'color: red')
assets.appendChild(mixin)
cylinder.setAttribute('mixin', 'makeitred')
}, 2000);
},
});
and then the HTML in which the mixin attribute will be added later
<a-scene addasset>
<a-assets>
</a-assets>
<a-cylinder
position="0 0.5 -3">
</a-cylinder>
</a-scene>
Here is a glitch of that
And for the sake of exploration, here is the same set up but triggered by an example event. First the same component but with an event listener
// add assets at run time, triggered by event
AFRAME.registerComponent('addasset', {
init: function () {
document.addEventListener("testevent", function(){
var assets = document.getElementsByTagName('a-assets')[0]
var mixin = document.createElement('a-mixin')
var cylinder = document.getElementsByTagName('a-cylinder')[0]
mixin.setAttribute('id', 'makeitred')
mixin.setAttribute('material', 'color: red')
assets.appendChild(mixin)
cylinder.setAttribute('mixin', 'makeitred')
});
},
});
Then a component that emits an event for testing
// test event to trigger adding of mixin
AFRAME.registerComponent('testevent', {
init: function () {
var self = this.el
setTimeout(function(){
self.emit("testevent")
}, 3000);
},
});
Then the HTML, as before but including a test entity that emits an event
<a-scene addasset>
<a-assets>
</a-assets>
<a-cylinder
position="0 0.5 -3">
</a-cylinder>
<a-entity
testevent>
</a-entity>
</a-scene>
And here is a glitch for that
So you could mix those up, add the mixin to assets but delay/trigger on event the addition of properties or add the mixin to assets with properties but delay/trigger on event the setting of that attribute on your target elements.
I hope that helps
I'm making a WebVR application with aframe and angular-cli. I load some static JSON data from the assets and will bind it to an A-frame element. Here is an example of my JSON data.
{
"id": 4,
"image": "",
"location": "Font",
"nextScenes": [
3,
5
],
"hotspots": [
{
"id": "obj1",
"location": "2325 1305 -2400",
"rotation": "-5 -50 -5",
"scale": "150 150 150",
"headerTitle": "",
"body": ""
},
{
"id": "obj2",
"location": "3145 890 -2175",
"rotation": "-5 -50 -5",
"scale": "150 150 150",
"headerTitle": "",
"body": ""
}
]
}
I'll load the hotspots in my HTML code with this code:
<a-scene inspector="url: https://aframe.io/releases/0.3.0/aframe-inspector.min.js">
<a-assets>
<img id="sky" [src]="currentImageSource" alt="" />
</a-assets>
<a-sky src="#sky"></a-sky>
<!-- problems with code below -->
<a-entity *ngFor="let spot of currentData.hotspots; let i = index" [id]="spot.id"
[position]="spot.location" [rotation]="spot.rotation" [scale]="spot.scale"
visible="true" mixin="null" color="pink"
text="zOffset:0;value:S;height:100;width:100;align:center"></a-entity>
</a-scene>
Notice that currentData is equal to the JSON code above and currentImageSource contains the location of the image.
The problem with code above is that the attributes position, rotation and scale wouldn't bind. In the rendered output are the variables empty but the ng-reflect-... attributes aren't empty.
Also if I inspect the code with ctrl + alt + I, the object got the a-entity tags the default values.
Update one: The databinding for the attribute id works.
Update two: Here you could see the output in my browser:
<app-vrtour _nghost-pub-1="">
<a-scene class="fullscreen" inspector="" canvas="" keyboard-shortcuts="" screenshot="" vr-mode-ui="" auto-enter-vr="" _ngcontent-pub-1="">
<a-assets _ngcontent-pub-1="">
<ewalswebvr-static-assets _ngcontent-pub-1=""><img id="#details" crossorigin="anonymous" scr="/assets/images/details.jpg"></ewalswebvr-static-assets>
<img id="sky" alt="" src="assets/360images/P5.tif" ng-reflect-src="assets/360images/P5.tif" _ngcontent-pub-1="">
</a-assets>
<!--template bindings={"ng-reflect-ng-for-of": "[object Object],[object Object]"}-->
<!-- The two tags below are the lines that wouldn't bind -->
<a-entity id="obj1" mixin="null" text="" ng-reflect-id="obj1" ng-reflect-position="2323.81 1305.90 -2400" ng-reflect-rotation="-4.58 -48.7 -5.16" ng-reflect-scale="150 150 150" ng-reflect-visible="true" position="" rotation="" scale="" visible=""
_ngcontent-pub-1="" ng-reflect-color="#ff0000"></a-entity>
<a-entity id="obj2" mixin="null" text="" ng-reflect-id="obj2" ng-reflect-position="3145.63 889.46 -2176.50" ng-reflect-rotation="-4.58 -48.7 -5.16" ng-reflect-scale="150 150 150" position="" rotation="" scale="" visible="" _ngcontent-pub-1=""
ng-reflect-color="#00ff00"></a-entity>
<a-sky src="#sky" material="" position="" rotation="" scale="" visible="" geometry="" _ngcontent-pub-1=""></a-sky>
<canvas width="1920" height="930" class="a-canvas a-grab-cursor" style="width: 1920px; height: 930px;" data-aframe-canvas="true"></canvas>
<div class="a-enter-vr" style="display: none;" aframe-injected=""><button class="a-enter-vr-button" aframe-injected=""></button></div>
<div class="a-orientation-modal a-hidden" aframe-injected=""><button aframe-injected="">Exit VR</button></div>
<a-entity aframe-injected="" position="" rotation="" scale="" visible="" camera="" wasd-controls="" look-controls="" data-aframe-inspector="default-camera"></a-entity>
<a-entity aframe-injected="" position="" rotation="" scale="" visible="" light="" data-aframe-default-light=""></a-entity>
<a-entity aframe-injected="" position="" rotation="" scale="" visible="" light="" data-aframe-default-light=""></a-entity>
<a-entity position="" rotation="" scale="" visible="" camera=""></a-entity>
</a-scene>
</app-vrtour>
Could you find the bug in my code?
Thanks in advance
The reason for angular data binding not working is given here.
Using Angular one way data binding, you are actually setting the properties of your a-frame entities. However, in real, no such properties exist. These are actually the attributes of those elements. Hence, you area able to manipulate via dom api.
However, using DOM api is not recommended from security perspective. But that is not required either.
To do attributes data binding, you can use similar syntax as follows.
<a-entity *ngFor="let spot of currentData.hotspots; let i = index" [id]="spot.id"
[attr.position]="spot.location" [attr.rotation]="spot.rotation" [attr.scale]="spot.scale"
visible="true" mixin="null" color="pink"
text="zOffset:0;value:S;height:100;width:100;align:center"></a-entity>
After trying a workaround, I've found a solution that works. The issue of this code is that it didn't use databinding and I thing it is less performance that databinding but A-frame doesn't support it.
Here you could find the code I've added in my typescript code:
ngAfterViewInit(): void {
for (let i: number = this.currentData.hotspots.length; i--;) {
let spot: any = this.currentData.hotspots[i],
el: any = document.getElementById(spot.id);
el.setAttribute("position", spot.location);
el.setAttribute("rotation", spot.rotation);
el.setAttribute("scale", spot.scale);
}
}
This code works if I give every a-entity-tag an unique ID. The HTML code of my component, is unchanged but it's possible to use this instead.
<a-entity *ngFor="let spot of currentData.hotspots; let i = index" [id]="spot.id"
text="zOffset:0;value:S;height:100;width:100;align:center"></a-entity>
Don't worry about what you see in the DOM Inspector. What you are binding is still affecting the scene itself, right? If so, then I think this would answer:
https://aframe.io/docs/0.5.0/introduction/faq.html#why-is-the-html-dom-not-updating-in-a-frame
For performance reasons, A-Frame does not update the DOM with component data. Use the debug component to enable component-to-DOM serialization.
By default, for performance reasons, A-Frame does not update the DOM with component data. If we open the browser’s DOM inspector, we will see that many entities will have only the component name visible:
<a-entity geometry material position rotation></a-entity>
The component data is stored internally. Updating the DOM takes CPU time for converting component data, which is stored internally, to strings. However, when we want to see the DOM update for debugging purposes, we can attach the debug component to the scene. Components will check whether the debug component is enabled before trying to serialize to the DOM. Then we will be able to view component data in the DOM:
<a-entity geometry="primitive: box" material="color: red" position="1 2 3"
rotation="0 180 0"></a-entity>
Make sure that this component is not active in production.
You can get this working in the template (without any a-entity setup in the controller class) by using the [attr.*] attribute binding syntax, e.g.
<a-entity *ngFor="let spot of currentData.hotspots"
[attr.position]="spot.location" [attr.rotation]="spot.rotation"
[attr.scale]="spot.scale"
...
(And no element ID required.)
REF: https://stackoverflow.com/a/43733797/189518
I am trying to create a very simple HTML page with Polymer, which includes an HTML file with all the components I need:
<link rel="import" href="bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="components/my-element.html">
my-element.html looks like this:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="my-element">
<template>
// Some HTML
</template>
<script>
Polymer({
is: 'my-element',
properties:{
// properties
}
});
</script>
</polymer-element>
When I run it as it is, everything works fine.
When I try to Vulcanize the components.html file, and then open the same page with the Vulcanized version, I get the following error in the console
"Uncaught TypeError: prototype.registerCallback is not a function"
I have noticed the Vulcanize process turns
Polymer({
is: 'my-element',
properties:{
// properties
}
});
Into
Polymer('my-element', {
is: 'my-element',
properties:{
// properties
}
});
Which seems to be what is causing the bug, as window.Polymer only expects an Object as a parameter.
I am using grunt-vulcanize to do the actual Vulcanizing, and my config looks like this:
vulcanize: {
default: {
options: {
excludes: {
imports: [
'polymer.html'
]
},
'strip-excludes': false,
inline: true,
strip: true
},
files: {
'build.html': 'components.html'
},
},
}
Is there a way of stopping this?
I am using Polymer 1.0, and grunt-vulcanize 0.6.4
Got it: I was using <polymer-element> instead of <dom-module>. <dom-module> is correct for Polymer 1.0.
This guide is useful, and has taken me days to find, if only so that you know what works with 0.5 and what works with 1.0:
https://www.polymer-project.org/1.0/docs/migration.html
grunt-vulcanize is not compatible with vulcanize 1.8.1, because last update on github was on Feb 9 with version 0.6.4. It's out of date plugin for Polymer 0.8+.
temporary adaptation for grunt-vulcanize which works with vulcanize ^1.8.1 and Polymer 1.0.
https://github.com/kgadzinowski/grunt-vulcanize/
just change in package.json:
"grunt-vulcanize": "kgadzinowski/grunt-vulcanize"
it'll work fine