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.
Related
So, I've been messing around with opacity fade-in/fade-outs of glTF models in Aframe, and have achieved good results using Piotr Adam Milewski's model-opacity script (from here), and have looped my daisy-chained animation sequences using Tired Eyes' animation-manager script (from here).
However, I'm having difficulties trying to work out how to also animate the opacity of the model's shadow, as at the moment its shadow still remains visible after the model is no longer visible.
Demo Link 👇
I've remixed a Glitch (of Ada Rose Cannon's AR Starter Kit) which you can find here to show what I mean (see line 204 in the Glitch for the model fade-in/out).
I'd be really grateful if anyone can shed any light on whether it's possible to animate the Aframe shadow to match the model's opacity. Many thanks, in advance, for any advice 🙂
I'm afraid a built-in solution is not yet ready (issue / PR)
For a single object, you could just use a ShadowMaterial, which has a opacity property, which could be animated along with the objects opacity:
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script src="https://gftruj.github.io/webzamples/aframe/models/components/model-opacity.js"></script>
<script>
AFRAME.registerComponent("relative-shadow", {
schema: {
target: { type: "selector" }
},
init: function() {
const mesh = this.el.getObject3D("mesh"); // grab the mesh
const oldMaterial = mesh.material; // store the old material
mesh.material = this.material = new THREE.ShadowMaterial(); // apply a shadow material
oldMaterial.dispose(); // dispose the old material
},
update: function() {
this.opacitySource = this.data.target.components["model-opacity"]; // react to the target being set
},
tick: function() {
if (!this.opacitySource) return; // wait until we can access the opacity value
// update the opacity using the t-rex material opacity from the component
this.material.opacity = this.opacitySource.data.opacity;
}
})
</script>
<a-scene>
<a-asset-item id="spino" src="https://rawcdn.githack.com/krlosflip22/aframe-ar-sample/c91a7a9dd8b1428bc8e68bc1b5d8641d7241fd1b/spinosaurus.glb"></a-asset-item>
<a-gltf-model id="trex" position="0 1 -4" shadow="cast: true" scale="0.5 0.5 0.5" src="#spino" model-opacity
animation="property: model-opacity.opacity; to: 0; dur: 1000; dir: alternate; loop: true;"></a-gltf-model>
<a-plane rotation="-90 0 0" scale="40 40 40"
relative-shadow="target: #trex" shadow="receive: true"></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
For multiple objects, a simple yet effective solution would be faking shadows - have transparent radial gradient images below the objects like in the threejs fundamentals example. You could control the opacity of each one of them:
<iframe width="100%" height="100%" src="https://r105.threejsfundamentals.org/threejs/threejs-shadows-fake.html"></iframe>
I have a simple AR.js scene that should display a clickable white box on a hiro marker. After click the box's colour should change to red. In fact, the box is clickable and the colour is changing, but it's position and the position of it's clickable area are not the same. In my case this area is beneath the box. Here's a code example:
<!DOCTYPE html>
<html>
<script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
<script src="https://rawgit.com/jeromeetienne/ar.js/master/aframe/build/aframe-ar.js"></script>
<script>
AFRAME.registerComponent('clickhandler', {
init: function () {
this.el.addEventListener('click', () => {
this.el.setAttribute('material', 'color: red;');
});
},
});
</script>
<body>
<a-scene embedded arjs>
<a-marker cursor="rayOrigin: mouse;" preset="hiro">
<a-box
material="color: white;"
position="0 0 0"
depth="0.2"
height="0.01"
width="0.2"
clickhandler
/>
</a-marker>
<a-entity camera></a-entity>
</a-scene>
</body>
</html>
However, if you open the aframe inspector (Ctrl + Alt + I) and then close it, box's clickable area and it's position will be correct and I don't understand how is that happening. So how can I make this scene to display the right way?
I've explored how the aframe inspector works and found that it inserts a new raycaster programmatically after it's initialization, so I've decided to do the same and it worked!
All you need to do to place clickable area in the same spot with it's object is to execute these lines after the scene is fully initialized.
const scene = AFRAME.scenes[0];
if (!scene) {
return;
}
const mouseCursor = document.createElement('a-entity');
mouseCursor.setAttribute('cursor', 'rayOrigin', 'mouse');
scene.appendChild(mouseCursor);
I am using the updated super-hands component in a-frame. I have replicated the basic example from the docs (see below).
<head>
<title>Most Basic Super-Hands Example</title>
<script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
<script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v4.1.2/dist/aframe-extras.min.js"></script>
<script src="https://unpkg.com/super-hands#3.0.0/dist/super-hands.min.js"></script>
</head>
<body>
<a-scene>
<a-assets></a-assets>
<a-entity>
<a-camera></a-camera>
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="left"></a-entity>
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="right"></a-entity>
</a-entity>
<!-- hover & drag-drop won't have any obvious effect without some additional event handlers or components. See the examples page for more -->
<a-box hoverable grabbable stretchable draggable dropppable color="blue" position="0 0 -1"></a-box>
</a-scene>
</body>
Which works well out of the box when it comes to grabbing objects and being able to affect their position, but not their rotation. I wondered if it was possible, (without using physics and adding a static body to the controller) to affect the rotation of the targeted objects?
I have been able to get the rotation working by adding the relevant mixin and phase-shift component from this example but given it seems possible to grab objects affecting their position I wondered if I could modify the basic example to change the rotation instead of the position (ideally I would be able to choose whether to affect position, rotation or both). I guess I'm imagining perhaps creating another component like rotatable or maybe a build on the grabbable component like grabbable="property: rotation" that I could add to the target objects.
I would like to know if this is possible in general as I would like to understand the component better so as to have more control. To give some context for this particular question however, I have a situation where the super-hands controllers are children of a dynamic-body and once a static-body is added, it causes problems with the behaviour of the parent dynamic-body.
Any advice appreciated as ever, if you need any more info, please let me know.
If you want the rotation without physics, you'll need to implement your own version of grabbable that does the matrix math. It would go something like this:
tick: (function () {
const grabeeMatrix = new window.THREE.Matrix4()
const ignoreScale = new window.THREE.Vector3()
return function () {
if (this.grabber) {
grabeeMatrix.multiplyMatrices(
this.grabber.object3D.matrixWorld,
this.grabOffsetMatrix
)
grabeeMatrix.multiplyMatrices(this.parentOffsetMatrix, grabeeMatrix)
// using decomp over direct Object3D.matrix manipulation
// keeps in sync with other A-Frame components
grabeeMatrix.decompose(
this.el.object3D.position,
this.el.object3D.quaternion,
// let stretchable manage scale
ignoreScale
)
}
}
})(),
resetGrabber: function () {
if (!this.grabber) {
return false
}
this.grabber.object3D.updateMatrixWorld()
this.el.object3D.parent.updateMatrixWorld()
// save difference between grabber world matrix and grabee world matrix
this.grabOffsetMatrix
.getInverse(this.grabber.object3D.matrixWorld)
.multiply(this.el.object3D.matrixWorld)
// save difference between grabee world and local matrices
this.parentOffsetMatrix.getInverse(this.el.object3D.parent.matrixWorld)
return true
},
I'm loading this Collada (DAE) model with aframe 0.8.2 and using aframe-ar to display it over a Hiro marker:
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
<script src="https://cdn.rawgit.com/jeromeetienne/AR.js/1.5.5/aframe/build/aframe-ar.js"></script>
<body style='margin : 0px; overflow: hidden;'>
<a-scene embedded arjs='trackingMethod: best; debugUIEnabled: false;'>
<!--a-marker type='pattern' url='https://rawgit.com/germanviscuso/AR.js/master/data/data/patt.gafas'-->
<a-marker preset='hiro'>
<a-collada-model src="url(https://aframe.io/aframe/examples/showcase/shopping/man/man.dae)"></a-collada-model>
</a-marker>
<a-camera-static/>
</a-scene>
</body>
Codepen: https://codepen.io/germanviscuso/pen/KRMgwz
I would like to know how to add controls to rotate it on its Y axis (with respect to the marker) by using swipe gestures on a mobile phone browser and to scale the model dynamically when doing pinch gestures. Ideally it would be nice if it also works with the mouse/touchpad when I'm testing in my laptop but touch on the phone is enough.
Can universal-controls handle this? Any example that I can see? This has to work while the model is being rendered dynamically with respect to the marker (AR tracking).
Any help is appreciated, thanks!
I wouldn't use the "native" controls based off raycaststers. Instead I would use any js gesture detection library. In this example i used hammer.js.
hammer.js registers an object which emits events when pan, swipe, pinch gestures are detected. I've created the object with the listeners in an a-frame component.
When pan is detected i just rotate the model depending on the direction (2 - left, 4 - right, 8 - up, 16 - down)
When pinch is detected i change the scale, depending on the event value, you can multiply by any factor. The component is below:
AFRAME.registerComponent("foo",{
init:function() {
var element = document.querySelector('body');
this.marker = document.querySelector('a-marker')
var model = document.querySelector('a-collada-model');
var hammertime = new Hammer(element);
var pinch = new Hammer.Pinch(); // Pinch is not by default in the recognisers
hammertime.add(pinch); // add it to the Manager instance
hammertime.on('pan', (ev) => {
let rotation = model.getAttribute("rotation")
switch(ev.direction) {
case 2:
rotation.y = rotation.y + 4
break;
case 4:
rotation.y = rotation.y - 4
break;
case 8:
rotation.x = rotation.x + 4
break;
case 16:
rotation.x = rotation.x - 4
break;
default:
break;
}
model.setAttribute("rotation", rotation)
});
hammertime.on("pinch", (ev) => {
let scale = {x:ev.scale, y:ev.scale, z:ev.scale}
model.setAttribute("scale", scale);
});
}
});
Working glitch here. In my example i also check if the marker is visible, thought it could be convinient.
Working example here
Total beginner to a-frame here, have been through the tutorial scenes and am now setting up my first using .obj models.
Using a remote server, feel like that's important information.
I've seen questions about models not showing up but mine is displaying broken and I'm not sure where to start fixing it.
This is how it looks in windows 3D builder:
And this is how it looks in my project (backed on pink plane for contrast):
Here's the html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Pokemon Stadium</title>
<link href="css/main.css" rel="stylesheet">
<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script>
<script src="js/main.js"></script>
</head>
<body>
<!-- Scene -->
<a-scene onLoad="">
<!------------------------------------------------ Assets --------------------------------------------------------->
<a-assets>
<a-asset-item id="stadium-obj" src="assets/models/stadium/Pokemon+Stadium.obj"></a-asset-item>
<a-asset-item id="stadium-mtl" src="assets/models/stadium/Pokemon+Stadium.mtl"></a-asset-item>
<a-asset-item id="ivy-obj" src="assets/models/ivysaur/Pokemon.obj"></a-asset-item>
<a-asset-item id="ivy-mtl" src="assets/models/ivysaur/Pokemon.mtl"></a-asset-item>
</a-assets>
<!------------------------------------------------- Scene --------------------------------------------------------->
<!-- Skybox -->
<a-sky color="#279DF4"></a-sky>
<!-- Abyss -->
<a-plane scale="1000 1000" position="0 -10 0" color="#212121" rotation="-90 0 0" material="shader: flat"></a-plane>
<!-- Stadium -->
<a-entity obj-model="obj: #stadium-obj; mtl: #stadium-mtl" position="0 0 -30" scale="0.05 0.05 0.05" material="side: double"></a-entity>
<!-- Bulbasaur -->
<a-entity obj-model="obj: #ivy-obj; mtl: #ivy-mtl" position="15 10 0" scale="1 1 1" rotation="0 90 0"></a-entity>
<!-- Camera + cursor -->
<a-entity camera look-controls position="10 12 0" rotation="-23 -90 0">
<a-cursor id="cursor"
animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150"
animation__fusing="property: fusing; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500"
event-set__1="_event: mouseenter; color: springgreen"
event-set__2="_event: mouseleave; color: black"
fuse="true"
raycaster="objects: .link"></a-cursor>
</a-entity>
</a-scene>
</body>
</html>
You probably have to set the type of side of the material to THREE.DoubleSide. With A-Frame you should be able to change the type using the following DOM attribute on an AEntity: material="side: double".
If this is not the case, you should update your post with a snippet of your scene elements.
EDIT:
As shown in the image, parts of my model are rendered incorrectly. The modelloader in THREEjs reads all meshes in a model and binds them to a grouped object. To fix this, you have to set the side of the material of the meshes to DoubleSided. Luckily in A-Frame, the obj-model component emits an event when the model has loaded successfully, we add a listener for the emitted event model-loaded on the DOM element and append a callback which returns a customevent. The customevent sends a reference to the model group. Query for the AEntity, I've appended an id modelEl to mine.
<script>
(function(modelEl) {
if (!window['AFRAME'] && !modelEl) {
return;
}
modelEl.addEventListener('model-loaded', function(evt) {
var model = evt.detail.model;
traverse(model);
});
})(document.getElementById('stadium'));
function traverse(node) {
node.children.forEach(function(child) {
if (child.children) {
traverse(child);
}
updateMaterial(child['material'], THREE.DoubleSide);
});
}
function updateMaterialSide(material, side) {
if (!material) {
return;
}
if (material instanceof THREE.Material) {
material.side = side;
material.needsUpdate = true
} else if (material instanceof THREE.MultiMaterial) {
material.materials.forEach(function(childMaterial) {
updateMaterial(childMaterial, side);
});
}
}
</script>
After running the above script, my model, and some of the texture loading, has been fixed.
Something to consider would be digging into creating custom components and modify or extend the obj-model component to prevent having to query for every model that may have the same issue.
If none of this worked, I believe something might be wrong with your wavefront obj export settings.
EDIT2:
Regarding my comment, here is a result of the fixed obj file in A-Frame:
For convenience sake you can find the MTL and OBJ file here:
obj file
mtl file