Three.js - how can I update an arrowHelper? - three.js

I'm trying to update an arrowHelper. I've tried manipulating the vertices in the arrow object's line, setting everything dynamic = true and so on, but the only way I can seem to do it is by removing the old line and drawing a new one. Is there a way to update an arrowHelper?

So you can't update an arrowHelper per se in the usual way you update something, by changing the values you used to create the object.
However: you can move the arrow's position, which moves its apparent start point, and you can give it a new direction and length, which moves its apparent end point.
Here's how to do it:
// new arrowHelper
var sourcePos = new THREE.Vector3(0, 0, 0);
var targetPos = new THREE.Vector3(0, 50, 0);
var direction = new THREE.Vector3().sub(targetPos, sourcePos);
var arrow = new THREE.ArrowHelper(direction.clone().normalize(), sourcePos, direction.length(), 0x00ff00);
scene.add(arrow);
// update the arrow
var newSourcePos = new THREE.Vector3(10, 10, 10);
var newTargetPos = new THREE.Vector3(60, 10, 10);
arrow.position.set(newSourcePos);
direction = new THREE.Vector3().sub(newTargetPos, newSourcePos);
arrow.setDirection(direction.normalize());
arrow.setLength(direction.length());

Thanks to the folks who answered this question, it certainly helped me. There are a couple of small problems with it that folks will want to avoid:
Calling direction.normalize() in the penultimate line of code changes the direction vector to a unit vector and thus the original length is lost. You can change the order of the last two lines, setting the arrow length from the unmodified direction vector, or (perhaps clearer) you can save its length in a separate variable. Without some such modification, though, the arrow will always be set to unit length.
In addition, the function arrow.setLength(length, headLength, headWidth) accepts two arguments beyond those shown in the answer (perhaps this has changed since this was written?). Accepting defaults for headLength and headWidth by not supplying explicit values in the call will result in overwriting any non-default values that might have been supplied in the constructor (rather than keeping the values as they are set).

Related

ThreeJS apply properties from one camera to another camera

In my webapp I'm using ThreeJS scenes in different modals/popups/dialogs with different width/height ratios.
Furthermore, I want to use multiple user defined camera settings (rotation, position, lookAt etc.) among these different scenes.
Therefore, I save the camera object via camera.toJSON() when the user clicks a capture camera settings button.
(Before I did this, I saved just the object camera, but unfortunately these objects are quite big and slow down the performance while multiple camera objects get stored. Nevertheless, this approach worked, since I was able to copy all the desired values between the saved camera object and the currently used camera [e.g. current_camera.position.x=saved_camera.position.x and so on])
In every scene I want now to use the saved properties I tried the following:
let m = new THREE.Matrix4();
m.fromArray(saved_camera.object.matrix);
current_camera.applyMatrix(m)
current_camera.updateMatrix();
Unfortunately this doesn't work.
"normal" camera object
camera.toJSON() object
If you're comfortable using matrices, then you can turn off the matrix auto-update that three.js does during the render process, and keep the world matrix up-to-date yourself. (This includes any time you change the camera's orientation, so keep that in mind if you're using some form of mouse interaction to control the camera angle.)
First, turn off automatic matrix updating for your camera by setting the autoUpdateMatrix property to false. You can still use the convenience properties (position, rotation, scale), but you'll have to manually update the world matrix by calling camera.updateMatrixWorld(true);.
Finally, when you're ready to restore a particular camera orientation, simply copy the matrix values using the matrixWorld's copy method.
var origin = new THREE.Vector3();
var theCamera = new THREE.PerspectiveCamera(35, 1, 1, 1000);
theCamera.autoUpdateMatrix = false; // turn off auto-update
theCamera.position.set(10, 10, 10);
theCamera.lookAt(origin);
theCamera.updateMatrixWorld(true); // manually update the matrix!
console.log("Camera original matrix: ", theCamera.matrixWorld.elements.toString());
var saveMatrix = new THREE.Matrix4();
saveMatrix.copy(theCamera.matrixWorld);
// saveMatrix now contains the current value of theCamera.matrixWorld
theCamera.position.set(50, -50, 75);
theCamera.lookAt(origin);
theCamera.updateMatrixWorld(true); // manually update the matrix!
console.log("Camera moved matrix: ", theCamera.matrixWorld.elements.toString());
// theCamera.matrixWorld now holds a value that's different from saveMatrix.
theCamera.matrixWorld.copy(saveMatrix);
// Don't upate the matrix, because you just SET it.
console.log("Camera moved matrix: ", theCamera.matrixWorld.elements.toString());
// theCamera.matrixWorld once again contains the saved value.
<script src="https://threejs.org/build/three.js"></script>
Edit to address OrbitControls:
It looks like OrbitControls uses the convenience properties, rather than gathering the information from the matrix. As such, when you restore a camera position, you'll also need to restore those properties. This is easily done by using decompose on the matrix, and copying the resulting values into the appropriate properties:
var d = new THREE.Vector3(),
q = new THREE.Quaternion(),
s = new THREE.Vector3();
camera.matrixWorld.decompose( d, q, s );
camera.position.copy( d );
camera.quaternion.copy( q );
camera.scale.copy( s );

Three.js simple custom animation does not continue changing with DAT.GUI .onChange function

Apologies if my mistake is obvious. Started learning code a few weeks ago. Have searched copiously for an answer.
Attempting to control a variable of a graphical equation animated in three.js using Dat.Gui. By assigning the variable to nothing, I was able to see that the gui is linked because clinking anywhere on the slider immediately calls the intended graphed equation (for simplicity's sake I have replaced my more complicated equation with a simple parabola). The graphed equation also disappears when the gui slider is clicked again, as hoped, to clear the way for the 'redrawn' equation (wider or skinnier parabola as determined by gui slider), however it throws this error:
"[.CommandBufferContext]RENDER WARNING: Render count or primcount is 0."
I've been able to rearrange the code so it doesn't throw this error, but it still does not call the next visible iteration of the equation.... Below is the code that succeeds in drawing the parabola, removes it upon the next .onChange, but then throws the above error...
init function + scene, camera setup, etc....
gui = new DAT.GUI();
gui_a = gui.add(this, 'a').min(0.01).max(5).step(0.01).name('Width');
gui_a.onChange(function(value){createGraph();});
}
function createGraph(){
if(graphLine) scene.remove(graphLine);
var graphGeometry = new THREE.Geometry();
while (x<20){
var y = a*(Math.pow(x,2));
next_x = x+0.05;
next_y = a*(Math.pow(next_x,2));
x=x+0.05;
graphGeometry.vertices.push(
new THREE.Vector3(x, next_y),
new THREE.Vector3(next_x, next_y)
);};
graphLine = new THREE.Line(graphGeometry, material);
scene.add(graphLine);
};
Help would be hugely appreciated.
Figured out my own problem... quite simple as I thought: the value of 'x' is not getting reset in creategraph();. It is defined with variables at the top of the code as "var x = -20" (which is not visible in my post..). Therefore the 'second' rendering of the graph is starting at x = 20, instead of reverting back to the original x=-20.

How to fix tiny holes in the Mesh after subtract operation with Three.CSG

I have a Box Mesh where I subtract another Box with Three.CSG to create a wall with a window.
After doing so, there are tiny holes in the Mesh alongside the cut. They are not visible alle the time, but show up when moving around.
How to close these holes?
This is part of the code how I am creating the Mesh:
var wallBsp = new ThreeBSP( myWallMesh );
var subMesh = new THREE.Mesh( mygeo );
var subBsp = new ThreeBSP( subMesh );
var subtract_bsp = wall_bsp.subtract( subBsp );
var result = subtract_bsp.toMesh();
result.material.shading = THREE.FlatShading;
result.geometry.computeVertexNormals();
Update
I have created a jsfiddle, but it is difficult to reproduce the error, I couldnt make it visible there: http://jsfiddle.net/L0rdzbej/23/
However, you can see the full application here.
Like #gaitat suggested, geometry.mergeVertices() does not look like it changes anything for me. Chandler Prall hinted at the source where precisionPoints, which is a variable inside the mergeVertices function, could solve this. Depending on the scale of the scene its value should be lower or negative, but I had no success so far.

Selecting an object in threejs?

I think the problem might be because it's a loaded object and not a "created" geometry, but I want to be able to select an object and show that it is selected, like in this example.
I load multiple objects in and I keep track of an index for each object (so the first object loaded has objIndex = 0, etc.) and I know that my code recognizes when I "mouse down" from the console. However, it says that intersects.length = 0 so the rest of the function is skipped.
I'm also not sure what "intersects.length" is actually taking the length of.
So I suppose my questions are:
What is "intersects.length" taking the length of?
Why do my objects have an "intersects.length" of 0?
What are some things I could do so that the objects are recognized?
Here's the relevant code:
function onDocumentMouseDown(event) {
event.preventDefault();
// default action of event will not be triggered
var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera);
// used to pass 3D positions and directions
var raycaster = new THREE.Raycaster(camera.position,
vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(objects);
console.log('Mouse is down.');
console.log(intersects.length);
if (intersects.length > 0) {
controls.enabled = false;
SELECTED = intersects[0].object;
var intersects = raycaster.intersectObjects(plane);
offset.copy(intersects[0].point).sub(plane.position);
container.style.cursor = 'move';
console.log('Clicked object: ' + object.name);
}
}
If you need to see more, let me know! Thank you. :)
Answering your question:
Here is an interesting read on raycast to help clarify raycasting:
http://soledadpenades.com/articles/three-js-tutorials/object-picking/
So what raycasting basically do is to (really) cast an imaginary ray. When you "mouse down" your program will, so called, cast a straight-line array into the scene. Now to answer your question:
For question one(1) and two(2) : Now when you cast a ray, it may or may not intersect an object. For example if you click into an empty space, the ray will not catch an object. However, as the name suggest, a raycast cast a ray and it may also hits multiple objects along the way. For example if you have one object and another object positioned directly behind the first object, casting a ray into the first object will also intersects the second object. The ray cast will "catch" both objects and put in the two objects into into the array "intersects". Therefore, the command
if (intersects.length > 0)
stated that if the raycast "catches" and object, do these. Moreover, it keeps on calling intersects[0] to refer to the first objects in the intersects array. In this case the most front object that the raycast catches. You can test my answer by creating hundreds of objects and state that, for every member in the intersects array, you change the color to red(for example). You will see that if the raycast catches multiple objects, all the objects will turn into that color. Hope for the first two question!
I am not sure of what you are asking for the third question. Can you clarify further? One way for the object to be recognized is to put them into the scene and put a raycast on the scene. However, I am not too sure of what you are asking to give you answer that I think you want.

Creating THREE.Line's with different endpoints using THREE.BufferGeometry

I am creating several THREE.Lines using THREE.BufferGeometry. Initially my app had them all starting at the origin and things worked as expected. Now, I would like to be able to start (and end) them at any point.
This fiddle (http://jsfiddle.net/9nVqU/) illustrates (I hope) how changing one end of the line away from the origin causes unexpected results.
I wondered if it was because any given line follows on from the previous one - switching the start/end order didn't change anything though so if that were true, I'd expect it to break.
Maybe I have the arrays set up incorrectly or the attributes that tell THREE.js how to interpret it - I think I need 2 * 3 verts for each line but changes I made to buffer_geometry.attributes = { seemed to make things worse.
FWIW, the actual effect I'm trying to achieve is to selectively turn on and off the lines based on user input. I can do that already by changing the end position but then I lose that value and I don't want to store it elsewhere. I thought that I could move the start point to the end point to switch it off and then move the start point to the origin again to re-enable it. If there is a way to enable/disable lines individually with BufferGeometry, then that would clearly be better.
First of all, you would have to do this:
var line = new THREE.Line( buffer_geometry, material );
line.type = THREE.LinePieces;
Second, this is not supported in r.58 , but should be.
As a work-around, you can hack WebGLRenderer.renderBufferDirect() like so:
// render lines
setLineWidth( material.linewidth );
var position = geometryAttributes[ "position" ];
primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
_gl.drawArrays( primitives, 0, position.numItems / 3 );
_this.info.render.calls ++;
_this.info.render.points += position.numItems;
three.js r.58

Resources