I'm using react three fiber, and would like to update some position when moving the mouse:
const Sphere = (props) => {
const [curPos, setCurPos] = useState<Vector3>();
return (
<mesh
onPointerMove={(e: ThreeEvent<PointerEvent>) => {
//update a position when moving mouse over object
// console.log(e);
setCurPos(e.point);
}}
position={props.position}
>
<sphereGeometry args={[100, 100, 100]} />
</mesh>
);
};
But I've noticed that using onPointerMove can cause a dip in FPS because mouse position is being updated so frequently.
Is there some way to throttle onPointerMove or limit the mouse polling rate for my canvas? Or generally a more performant solution?
Related
I've create a simple PlaneBufferGeomoetry in THREE.js
Geometry
const geometry = new THREE.PlaneBufferGeometry(10, 10, 10, 10);
const material = new THREE.MeshBasicMaterial( { side: THREE.DoubleSide, wireframe: true } );
const mesh = new THREE.Mesh( geometry, material );
const positions = geometry.attributes.position.array;
scene.add(mesh);
I'm then looping through each of the array segments in order to randomly change the z axis position during the render.
function animate() {
render()
requestAnimationFrame( animate );
}
function render() {
mesh.rotateZ(0.001)
const geoArray = geometry.attributes.position;
for (var i = 0; i < geoArray.count; i++) {
geoArray.setZ(i, Math.round(Math.random() * -2));
}
mesh.geometry.attributes.position.needsUpdate = true;
camera.updateProjectionMatrix()
renderer.render(scene, camera)
}
This works fine, however the animation speed is way too fast. How can i slow down the time during render in order for the z axis positions to smoothly change?
I have tried setting a setTimeOut function during the animate function, however this just slows the framerate down and not the animation itslef.
Thankyou
Right now, you are animating with fixed scalar values in your animation which is normally something you don't want to do in production apps. You should always take a time delta value into account so your animation is framerate independent. This makes it also easier to scale the time of your animation. So create an instance of THREE.Clock in your app like so:
const clock = new THREE.Clock();
In your animation loop, retrieve the time delta value for the current frame and use it:
const timeDelta = clock.getDelta();
mesh.rotateZ(timeDelta);
If you want to change the animation speed, multiply the delta value with another scalar representing your time scale value. E.g:
timeDelta *= 0.1; // this will slow down the animation
I'm trying to render a cube in the center of my scene with white edges that rotates. I got this to work but the edges of the cube are very jagged when it moves.
I tried enabling anti-aliasing and adding a FXAA shader but the lines are still jagged.
I'm using this FXAA shader and react-three-fiber instead of vanilla Three.js.
This is my effects composer:
const Effects = ({ factor }) => {
const composer = useRef();
const {
scene, gl, size, camera,
} = useThree();
useEffect(() => void composer.current.setSize(size.width, size.height), [size]);
useRender(({ gl }) => void ((gl.autoClear = true), composer.current.render()), true);
return (
<effectComposer ref={composer} args={[gl]}>
<renderPass attachArray="passes" scene={scene} camera={camera} />
<shaderPass
attachArray="passes"
args={[fxaa()]}
material-uniforms-resolution-value={[1 / size.width, 1 / size.height]}
renderToScreen
/>
</effectComposer>
);
};
Image here of the top edge of my cube with anti aliasing and FXAA applied
I ended up not using LineSegments to draw my edges and instead using the LineMaterial method from the fat lines example, which has the added benefit of variable line width.
In case anyone comes across this and is wondering how to use edges with LineMaterial here is the code:
const edges = new THREE.EdgesGeometry(geometry.current);
const geo = new LineSegmentsGeometry().fromEdgesGeometry(edges);
const matLine = new LineMaterial({
color: 'white',
linewidth: 2,
dashed: false,
});
matLine.resolution.set(window.innerWidth, window.innerHeight);
const wireframe = new Wireframe(geo, matLine);
wireframe.computeLineDistances();
wireframe.scale.set(1, 1, 1);
scene.add(wireframe);
We love three.js! And here is a page we built using it a few years ago.
https://www.jgprolock.com
We are in the process of revising the animations on this site.
Once the page loads, the user has the ability to drag and rotate the object. But it is really a trick. We are using orbit controls to rotate the camera around our scene, and thus our main object which is centered in the scene (positions x,y,z all equal to 0). If we did not place the object in the center, it starts to look uneven in its rotation as the camera now is rotating around a center that the object doesn't have.
In order to make it look like the object is on the left side, we ended up moving the canvas to the left and then we bring it back to the right or left as the animation continues after scrolling.
So, my question is .. does anyone have an example how to achieve this functionality just by rotating the actual object itself, instead of rotating the camera around the entire scene using the orbit controls plugin?
Or is there away to modify the orbit controls to rotate around an object and not the entire scene?
I've been searching for this for a while but right after asking this question I came across this link, which actually has an example of what we are trying to do.
https://jsfiddle.net/n6u6asza/1205/
The key to making this work as copied from the link: (although I am not 100% sure what this all means)
/* */
var isDragging = false;
var previousMousePosition = {
x: 0,
y: 0
};
$(renderer.domElement).on('mousedown', function(e) {
isDragging = true;
})
.on('mousemove', function(e) {
//console.log(e);
var deltaMove = {
x: e.offsetX-previousMousePosition.x,
y: e.offsetY-previousMousePosition.y
};
if(isDragging) {
var deltaRotationQuaternion = new three.Quaternion()
.setFromEuler(new three.Euler(
toRadians(deltaMove.y * 1),
toRadians(deltaMove.x * 1),
0,
'XYZ'
));
cube.quaternion.multiplyQuaternions(deltaRotationQuaternion, cube.quaternion);
}
previousMousePosition = {
x: e.offsetX,
y: e.offsetY
};
});
/* */
If you want an article on how to achieve this without the use of unnecessary jquery dependencies you can have a look here
This uses the eventListener to find a mousemove event whilst a mousedown event is occurring, and then passes the coordinates to a custom function.
var mouseDown = false,
mouseX = 0,
mouseY = 0;
var canvas = renderer.domElement
canvas.addEventListener('mousemove', function (evt) {
if (!mouseDown) {return}
//console.log('drag')
evt.preventDefault();
var deltaX = evt.clientX - mouseX,
deltaY = evt.clientY - mouseY;
mouseX = evt.clientX;
mouseY = evt.clientY;
// DO SOMETHING HERE WITH X and Y
object.rotation.x += deltaX
}, false);
canvas.addEventListener('mousedown', function (evt) {
evt.preventDefault();
mouseDown = true;
mouseX = evt.clientX;
mouseY = evt.clientY;
}, false);
canvas.addEventListener('mouseup', function (evt) {
evt.preventDefault();
mouseDown = false;
}, false);
}
But not that this will not work if you have OrbitControls or DragControls imported!
I am trying to make an app which adds models from Google poly in GLTF format.
I had the issue of some models being extremely large when added on the scene which I solved by computing their size with bounding box max and min values and setting the scale amount.
Now after adding the objects to the scene when I open the inspector and drag to scale objects, even with a small amount of drag the objects becomes very large.
If there is any way to reset the scale value of loaded objects so that default value can be the value which I computed and this can also solve the drag to scale issue.
Note: The computed scale factor for some elements goes to 0.00001 for x, y, z.
Use the three.js API within A-Frame components to compute a bounding box of the model, then scale it down to the size you prefer. Example:
AFRAME.registerComponent('autoscale', {
schema: {type: 'number', default: 1},
init: function () {
this.scale();
this.el.addEventListener('object3dset', () => this.scale());
},
scale: function () {
const el = this.el;
const span = this.data;
const mesh = el.getObject3D('mesh');
if (!mesh) return;
// Compute bounds.
const bbox = new THREE.Box3().setFromObject(mesh);
// Normalize scale.
const scale = span / bbox.getSize().length();
mesh.scale.set(scale, scale, scale);
// Recenter.
const offset = bbox.getCenter().multiplyScalar(scale);
mesh.position.sub(offset);
}
});
HTML:
<a-entity autoscale="2" gltf-model="stereo.gltf"></a-entity>
The code above will fit your model to a ~2m box, and re-center it. For more information see THREE.Object3D documentation.
three.js r89, A-Frame 0.8.0.
I am implementing d3.js zoom behaviour on a svg, on the same svg I also need to capture mouse coordinate so I can set position of a tool tips that follow mouse cursor's position.
The problem is that my 'mousemove' event has override d3.js zoom behaviour.
With 'mousemove' event added, zoom behaviour stop working.
It is either I have 'mousemove' event or 'zoom' event, but not both. Any suggestion how to get around this? Thanks.
// bind zoom behavior
selection_svg.call(
d3.behavior.zoom()
.translate([0, 0])
.scale(1)
.scaleExtent([1, 14])
.on('zoom', ()=>{ selection_plotArea.attr('transform', 'translate('+d3.event.translate+')scale('+d3.event.scale+')'); })
);
// tool tip coordinate
const toolTipNode = selection_toolTip.node();
toolTipNode.style.position = 'absolute';
selection_svg.node().addEventListener('mousemove',function(evt){
const coord_client = {
x:evt.clientX,
y:evt.clientY,
}
toolTipNode.style.left = `${coord_client.x}px`;
toolTipNode.style.top = `${coord_client.y}px`;
}, false);
I have added the code for this problem to fiddle:
https://jsfiddle.net/apollotang/rt9t1vdj/
The problem seems to be related to tooltipNode catching mouse events. By adding some offset to coord_client your problem would be gone.
selection_svg.on('mousemove', function () {
const coord_client = {
x: d3.event.layerX + 10,
y: d3.event.layerY + 10
};
toolTipNode.style.left = `${coord_client.x}px`;
toolTipNode.style.top = `${coord_client.y}px`;
}, false);
Note: I also changed clientX and clientY to layerX and layerY since there was a bug when scrolling changed mouse position and tooltipNode would have been separated from mouse. Besides the event handling code changed to be handled by d3.
If you have a mousemove event attached to your svg, be sure you aren't calling d3.event.stopPropagation() within the mousemove event, or else you will prevent the zoom behavior.