A-Frame Frustum Culling - three.js

How do I turn off frustum culling on a gltf model in A-Frame? I know in Three.js you can just traverse the object and add node.frustumCulled = false. I've tried
AFRAME.registerComponent('disable-culling', {
init: function(){
var object3D = this.el.sceneEl.object3D;
object3D.traverse((node) => {
node.frustumCulled = false
})
}
})
but that doesn't work. Does anyone have any idea? The entity is
<a-entity
id="ball"
scale="0.3 0.3 0.3"
position="0 0 -7"
gltf-model="#ballModel"
disable-culling
animation-mixer="clip: *; loop: once; clampWhenFinished: true;"
shadow>
</a-entity>

I had a similar problem which was solved with frustum culling -
el.addEventListener('model-loaded', () => {
const model = el.getObject3D('mesh');
model.traverse((node) => {
if (node.isMesh) {
node.frustumCulled = false;
}
});
});
I wonder if your solution didn't work simply because the model hadn't finished loading.

Go back with your 3D model to Blender, select your model on object mode and then press "ctrl A" - apply all transformations.

I just had this problem where animated models were getting culled before fully exiting the scene. In my case, the cause seemed to be that the object scale was too small. Once I scaled up the object up in Blender and re-exported the gltf file, the models were culled correctly.

Related

A-Frame: Adding animationClip from another glb to one that has no animation

I have a gltf model without an animation that I've loaded in aframe, and I'd like to add an animation that is connected to a different model. I've written some javascript to pull in the animation data and even bind it to the original GLB using GLTF loader, but not sure how to replace the aframe entity beyond just replacing the url.
Any help on how to solve this, or if there's a more efficient way to do this would be much appreciated! 🙂
HTML code:
<a-entity gltf-model="https://cdn.theoasis.xyz/public/bollywoodified/01.glb" id="center" position="0 0.03 -1.6" rotation="0 0 0" scale="1.3 1.3 1.3" animation-mixer\>\</a-entity\>
Javascript code:
import { GLTFLoader } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/loaders/GLTFLoader.js';
getAnimations('https://cdn.theoasis.xyz/glb/djland.glb');
function getAnimations(glb) {
const loader = new GLTFLoader();
loader.load(glb, function ( gltf ) {
loader.load(oasis_data.metadata[0].glb, function (data) {
data.animations = gltf.animations;
});
});
}
I tried to pull the animationClip from another glb and was successful, and then was able to replace the animation array on the intended glb with the one from the animated glb. But I do not know how to actually update the entity within AFrame, nor do i know if this is the most efficient solution!

Is it possible to append to an animated glb object in threejs/aframe?

I have an object moving about a scene, which I exported as a .glb (model A).
Within Aframe I'm playing the animation on model A using the animation-mixer tag and can see the object moving around. Now what I'm trying to do is append another model (model B), so that model B is also moving around the scene. However, model B just stays in place - at the specified model A position.
Is there a way to accomplish this in Threejs/Aframe?
I have revisited this technique and have the below code, so far, but the position of the model flickers between it's static position and where it's being set to in the tick.
<a-entity id="mantaray-path" gltf-model="#mantaray-path-glb" animation-mixer="timeScale: 1; loop: true;" pathanim></a-entity>
<a-entity id="mantaray" gltf-model="#mantaray-glb" animation-mixer="timeScale: 1;"></a-entity>
AFRAME.registerComponent("pathanim", {
init: function() {
this.el.addEventListener('model-loaded', this.update.bind(this));
},
tick: function() {
this.el.object3D.traverse(function(child) {
if (child.name == "mantaraypath") {
mantaray.object3D.position = child.position;
}
});
}
});

How to declare a "mask" material using A-Frame.js

I'm trying to make a scene where there's a "hole in the wall".
This requires a plane with square removed, and then a material applied to the plane with the following properties:
Invisible to the camera
Hides any other objects from being rendered that are behind it
There's an example of this with three.js here, but how can I do it with the a-frame material syntax?
The "Mask".
Looking at the box-hole example, to create the illusion, Lee creates two boxes.
1) The box which is "in the hole"
2) A slightly bigger invisible box without a top - to cloak the first one. The top is removed to work as a "hole" through which you can see the first box
How it can be done in THREE.js
The cloaking is done by preventing the second box from rendering any color. From Lee's example:
let material = new THREE.MeshBasicMaterial({
colorWrite: false;
})
The docs state, that the flag can be used to create invisible objects hiding others.
How it can be done in a-frame
I'm afraid you can't simply make the "cloak" material in a-frame. The colorWrite property is not exposed in the material component.
What i think the simplest way would be - is creating a cloak component, which will create the second box in THREE.js:
AFRAME.registerComponent('cloak', {
init: function() {
let geometry = new THREE.BoxGeometry(1, 1, 1)
geometry.faces.splice(4, 2) // cut out the top faces
let material = new THREE.MeshBasicMaterial({
colorWrite: false
})
let mesh = new THREE.Mesh(geometry, material)
mesh.scale.set(1.1, 1.1, 1.1)
this.el.object3D.add(mesh)
}
})
and use it like this:
<a-box material="src: myPic.png; side: back;" cloak>
Check it out in this codepen. With a HIRO marker, you should get a hole like this:
Using models, or other objects as "cloaks"
Here we need to apply the colorWrite=false magic to each node/child of the model.
init: function() {
// make sure the model is loaded first
this.el.addEventListener('model-loaded', e=>{
let mesh = this.el.getObject3D('mesh') // grab the mesh
if (mesh === undefined) return; // return if no mesh :(
mesh.traverse(function(node) { // traverse through and apply settings
if (node.isMesh && node.material) { // make sure the element can be a cloak
node.material.colorWrite = false
node.material.needsUpdate = true;
}
});
})
}
Also make sure the cloak is rendered before the elements that needs cloaking:
<a-marker>
<a-entity gltf-model="#wall-with-a-hole" cloak-component></a-entity>
<!-- the other stuff that needs to be cloaked-->
</a-marker

Emit light from an object

I'm making a Three.js scene in which I have stars object and I would like to be able to make them "glow".
By glow I mean make them really emit light not just put a "halo" effect around them.
I tried to put a PointLight object at the same position as the star, this make light emit from the object but as you can see, it doesn't make the object "glow" which make a weird effect.
My current code looks like this:
class Marker extends THREE.Object3D {
constructor() {
super();
// load obj model
const loader = new OBJLoader();
loader.load(
"https://supersecretdomain.com/star.obj",
object => {
object.traverse(child => {
if (child instanceof THREE.Mesh) {
// child.material.map = texture;
child.material = new THREE.MeshLambertMaterial({
color: 0xffff00
});
child.scale.set(0.01, 0.01, 0.01);
}
});
this.add(object);
}
);
const light = new THREE.PointLight(0xffff00, 0.5, 5);
this.add(light);
}
}
Any idea of how to do this ? ;)
Adding a point light to the star is the correct way to make other objects be affected by its light. To make the star itself shine, you can set the emissive color of the material to something other than black (for best results, you probably want it to be the same color as the light).
In addition to setting up your light, and setting the material emissive property as mentioned by Jave above..
You might want to look into the THREE PostProcessing examples.. Specifically Unreal Bloom pass... If you can get that working, it really sells the effect.
https://threejs.org/examples/webgl_postprocessing_unreal_bloom.html
Notice how the highlight glow actually bleeds into the scene around the object...

three.js and openlayers coordinates don't line up

I'm using three.js on a separate canvas on top of a openlayers map. the way I synchronize the view of the map and the three.js camera (which looks straight down onto the map) looks like this:
// calculate how far away the camera needs to be, to
// see this part of the map
function distanceFromExtentAndFOV(h, vFOV) {
return h / (2 * Math.tan(vFOV / 2));
}
// keep three.js camera in sync with visible part of openlayers map
function updateThreeCam(
group, // three.js group, containing camera
view, // ol view
map // ol map
) {
extent = getViewExtent(view, map);
const h = ol.extent.getHeight(extent);
mapCenter = ol.extent.getCenter(extent);
camDist = distanceFromExtentAndFOV(h, constants.CAM_V_FOV_RAD);
const pos = [...mapCenter, camDist];
group.position.set(...pos);
group.updateMatrixWorld();
}
// whenever view changes, update three.js camera
view.on('change:center', (event) => {
updateThreeCam(group, event.target, map);
});
view.on('change:resolution', (event) => {
updateThreeCam(group, event.target, map);
});
updateThreeCam(group, view, map);
I'm using three.js for custom animations of objects, that in the end land on the ground. however, when I turn the objects into openlayers features, the original three.js objects and the features don't line up perfectly (although their position coordinates are exactly the same). — also, when you pan, you can see that s.th. is slightly off.
// turn three.js planes into openlayers features:
const features = rects.map((rect) => {
const point = new ol.geom.Point([
rect.position.x,
tect.position.y
]);
return new ol.Feature(point);
});
vectorSource.addFeatures(features);
as I am pretty sure that my the code for synchronizing three.js and ol is correct, I am not really sure what what is going wrong. am I missing s.th. here?
It could be that you are using PerspectiveCamera and that will change the size/position based on depth. Try OrtographicCamera instead.

Resources