Removing/adding an object from/to scene in three.js - three.js

I am trying to add or remove an object from scene:
const rootObject = this.scene.getObjectByName('Object Group');
rootObject.remove(obj);// OR rootObject.add(obj);
What I observe is that to make change visible in scene, user needs to perform some action, like panning or zoomin/out etc.
How can change be made visible immediately without needing a manual action?

It looks like the scene is being redrawn not in the animation loop, but in some events (click, panning or zoomin/out, etc.).
So when adding or removing an object, you need to force a frame to be rendered. For example:
document
.getElementById('toggle')
.addEventListener('click', function() {
if (obj) {
removeObject()
} else {
addObject()
}
renderer.render(scene, camera);
})
[ https://jsfiddle.net/xmke5u20/ ]

Related

How to write a custom mouse wheel event handler in OrbitControls?

I'm working on a WebGL application using ThreeJS and OrbitControls. How do I write and make use of a custom event handler for wheel spin events?
Suppose my custom handler is
function custom_handleMouseWheel(event) { ... }
I tried adding it as a listener:
window.addEventListener("wheel", custom_handleMouseWheel);
but I suspect this adds my handler to a list of existing handlers (probably only one), and I'd have to remove the original handler. Not sure how to do that. And anyway, my handler never was called, which I checked for by adding a console.log("Wheel!") line to my handler.
Another thing I tried is to replace the handleWheelMouse method in the controls object, like this:
let original_handleMouseWheel;
function custom_handleMouseWheel(event) {
console.log("Custom Wheel!");
... fancy geometry calculations ...
original_handleMouseWheel(event);
}
// somewhere after defining scene, camera, renderer, etc...
controls = new THREE.OrbitControls( camera );
original_handleMouseWheel = controls.handleMouseWheel;
controls.handleMouseWheel = custom_handleMouseWheel;
but again the console.log line never executes.
What is the right way to go about doing this?
Just setting controls.enableZoom = false will stop the default zoom.
The default "wheel" listener is added to the "domElement" which should be the second parameter sent to the OrbitControls constructor.
Try renderer.domElement.addEventListener('wheel', custom_handleMouseWheel);

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...

Replace one component with another using animation

I'm looking to animate a text field into view and a button out of view at the same time, so that it looks like the text field is replacing the button. (They are both equal size and take up the same area of the screen).
What's the best way to do this using React Native animation?
At this point, I am rendering the button if one of my state values is false, and the text field if it is true.
You can animate any style property in react-native using the Animated API.
If you are able to represent the changes in a sequence of style changes, the Animated API can do it. For instance animating the opacity from 1 to 0 and back to 1 will give a nice fade in fade out effect. The docs explain the Animations much more clearly
Also you can you selective rendering to mount or hide the component
<View style={{/*style props that need to be animated*/}}
{ boolShowText? <Text/> : <View/> }
</View>
The fading example as found in react-native docs
class FadeInView extends React.Component {
constructor(props) {
super(props);
this.state = {
fadeAnim: new Animated.Value(0), // init opacity 0
};
}
componentDidMount() {
Animated.timing( // Uses easing functions
this.state.fadeAnim, // The value to drive
{toValue: 1}, // Configuration
).start(); // Don't forget start!
}
render() {
return (
<Animated.View // Special animatable View
style={{opacity: this.state.fadeAnim}}> // Binds
{this.props.children}
</Animated.View>
);
}
}

Resources