I have an existing line initiated
// material
const material = new THREE.LineBasicMaterial({ color: 0xffffff });
// array of vertices
vertices.push(new THREE.Vector3(0, 0, 0));
vertices.push(new THREE.Vector3(0, 0, 5));
//
const geometry = new THREE.BufferGeometry().setFromPoints(vertices);
const line = new THREE.Line(geometry, material);
And what I want to do is extend this line following its initiation. I've read this page on how to update things and I don't think it fits this situation because instead of adding vertices to my shape, I want to move them. Then again, it's very likely I misunderstood. I've tried deleting the line and then redrawing it longer, but I can't get it to work without my browser crashing.
The BufferGeometry exposes its vertices through its positions BufferAttribute. To change the positions, you should do something like the following:
//
// Assuming we want to move your line segment (0, 0, 0)-(0, 0, 5) by
// one unit in the direction of positive x, to (1, 0, 0)-(1, 0, 5).
//
// Get a reference to the "position" buffer attribute
const pos = geometry.getAttribute("position");
// Set the new positions
pos.setXYZ(0, vertices[0].x + 1, vertices[0].y, vertices[0].z);
pos.setXYZ(1, vertices[1].x + 1, vertices[1].y, vertices[1].z);
// Update the vertex buffer in graphics memory
pos.needsUpdate = true;
// Update the bounds to support, e.g., frustum culling
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
Other methods exist, such as modifying the attribute's backing array directly and copying in a new array, but the general process will be the same.
Related
I am creating a simple "Hello World' Three.js application and I am curious to know why this works.
Firstly, I create and show a centered "Hello World" from the code snippet below. This code snippet is responsible for centering the text and moving it back 20 units.
/* Create the scene Text */
let loader = new THREE.FontLoader();
loader.load( 'fonts/helvetiker_regular.typeface.json', function (font) {
/* Create the geometry */
let geometry_text = new THREE.TextGeometry( "Hello World", {
font: font,
size: 5,
height: 1,
});
/* Create a bounding box in order to calculate the center position of the created text */
geometry_text.computeBoundingBox();
let x_mid = geometry_text.boundingBox.max.x - geometry_text.boundingBox.min.x;
geometry_text.translate(-0.5 * x_mid, 0, 0); // Center the text by offsetting half the width
/* Currently using basic material because I do not have a light, Phong will be black */
let material_text = new THREE.MeshBasicMaterial({
color: new THREE.Color( 0x006699 )
});
let textMesh = new THREE.Mesh(geometry_text, material_text);
textMesh.position.set(0, 0, -20);
//debugger;
scene.add(textMesh);
console.log('added mesh')
} );
Now notice here that I perform the translation first
geometry_text.computeBoundingBox();
let x_mid = geometry_text.boundingBox.max.x - geometry_text.boundingBox.min.x;
geometry_text.translate(-0.5 * x_mid, 0, 0);
and then the position is performed to move the mesh
let textMesh = new THREE.Mesh(geometry_text, material_text);
textMesh.position.set(0, 0, -20);
Now my confusion comes from that fact that if I remove my translation, then my "Hello World" text is not centered. However after my translation is completed, I am setting the position on my mesh to (0, 0, -20), shouldn't this set_position call overwrite my previous translation and move the object to the position (0, 0, -20), why is my text still centered eventhough my set_position is called after my translation?
This is because the call to THREE.TextGeometry.translate() ends up calling THREE.Geometry.applyMatrix() with the corresponding translation matrix, which bakes the transformation by directly modifying the vertex coordinates. See Geometry.js#L149 for the source.
In other words, before the call
textMesh.position.set(0, 0, -20);
the mesh transformation matrix was still the identity matrix. Mesh transformation differs from geometry transformation in that it only updates the matrix that is passed into the shader, instead of recomputing every vertex. For which one you would want to use: transforming the geometry is more expensive, but you can do it once and avoid it in the render loop (See the explanation here).
i am using Physijs to determine static collision between my meshes. As i need to know what surfaces are intersecting.
i hacked a simple demo that seems to work.
currently i have to configure my scene to use gravity, which prevents me from position my meshes in any y position, as they start to fall or float.
is there is simple way to remove the gravity from the simulation, and just use the mesh collision detection?
--update---
i had to explicitly set the mass to each mesh to 0 rather than blank. With mass=0 gravity has no affect. great!
however meshes are not reporting a collision.
any ideas where i am going wrong?
thanks
-lp
You cannot use Physijs for collision detection alone. It just comes fully equipped with real-time physics simulation, based on the ammo.js library. When you set the mass of the meshes to 0, it made them static. They were then unresponsive to external forces, such as collision responses (i.e. the change of velocity applied on the mesh after the collision was detected) or gravity. Also, two static meshes that overlap each other do not fire a collision event.
Solution A: Use ammo.js directly
Ported from Bullet Physics, the library provides the necessary tools for generating physics simulations, or just detect collisions between defined shapes (which Physijs doesn't want us to see). Here's a snippet for detecting collision between 2 rigid spheres:
var bt_collision_configuration;
var bt_dispatcher;
var bt_broadphase;
var bt_collision_world;
var scene_size = 500;
var max_objects = 10; // Tweak this as needed
bt_collision_configuration = new Ammo.btDefaultCollisionConfiguration();
bt_dispatcher = new Ammo.btCollisionDispatcher(bt_collision_configuration);
var wmin = new Ammo.btVector3(-scene_size, -scene_size, -scene_size);
var wmax = new Ammo.btVector3(scene_size, scene_size, scene_size);
// This is one type of broadphase, Ammo.js has others that might be faster
bt_broadphase = new Ammo.bt32BitAxisSweep3(
wmin, wmax, max_objects, 0, true /* disable raycast accelerator */);
bt_collision_world = new Ammo.btCollisionWorld(bt_dispatcher, bt_broadphase, bt_collision_configuration);
// Create two collision objects
var sphere_A = new Ammo.btCollisionObject();
var sphere_B = new Ammo.btCollisionObject();
// Move each to a specific location
sphere_A.getWorldTransform().setOrigin(new Ammo.btVector3(2, 1.5, 0));
sphere_B.getWorldTransform().setOrigin(new Ammo.btVector3(2, 0, 0));
// Create the sphere shape with a radius of 1
var sphere_shape = new Ammo.btSphereShape(1);
// Set the shape of each collision object
sphere_A.setCollisionShape(sphere_shape);
sphere_B.setCollisionShape(sphere_shape);
// Add the collision objects to our collision world
bt_collision_world.addCollisionObject(sphere_A);
bt_collision_world.addCollisionObject(sphere_B);
// Perform collision detection
bt_collision_world.performDiscreteCollisionDetection();
var numManifolds = bt_collision_world.getDispatcher().getNumManifolds();
// For each contact manifold
for(var i = 0; i < numManifolds; i++){
var contactManifold = bt_collision_world.getDispatcher().getManifoldByIndexInternal(i);
var obA = contactManifold.getBody0();
var obB = contactManifold.getBody1();
contactManifold.refreshContactPoints(obA.getWorldTransform(), obB.getWorldTransform());
var numContacts = contactManifold.getNumContacts();
// For each contact point in that manifold
for(var j = 0; j < numContacts; j++){
// Get the contact information
var pt = contactManifold.getContactPoint(j);
var ptA = pt.getPositionWorldOnA();
var ptB = pt.getPositionWorldOnB();
var ptdist = pt.getDistance();
// Do whatever else you need with the information...
}
}
// Oh yeah! Ammo.js wants us to deallocate
// the objects with 'Ammo.destroy(obj)'
I transformed this C++ code into its JS equivalent. There might have been some missing syntax, so you can check the Ammo.js API binding changes for anything that doesn't work.
Solution B: Use THREE's ray caster
The ray caster is less accurate, but can be more precise with the addition of extra vertex count in your shapes. Here's some code to detect collision between 2 boxes:
// General box mesh data
var boxGeometry = new THREE.CubeGeometry(100, 100, 20, 1, 1, 1);
var boxMaterial = new THREE.MeshBasicMaterial({color: 0x8888ff, wireframe: true});
// Create box that detects collision
var dcube = new THREE.Mesh(boxGeometry, boxMaterial);
// Create box to check collision with
var ocube = new THREE.Mesh(boxGeometry, boxMaterial);
// Create ray caster
var rcaster = new THREE.Raycaster(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0));
// Cast a ray through every vertex or extremity
for(var vi = 0, l = dcube.geometry.vertices.length; vi < l; vi++){
var glovert = dcube.geometry.vertices[vi].clone().applyMatrix4(dcube.matrix);
var dirv = glovert.sub(dcube.position);
// Setup ray caster
rcaster.set(dcubeOrigin, dirv.clone().normalize());
// Get collision result
var hitResult = rcaster.intersectObject(ocube);
// Check if collision is within range of other cube
if(hitResult.length && hitResult[0].distance < dirv.length()){
// There was a hit detected between dcube and ocube
}
}
Check out these links for more information (and maybe their source code):
Three.js-Collision-Detection
Basic Collision Detection, Raycasting with Three.js
THREE's ray caster docs
I am writing a trace-line function for a visualization project that requires jumping between time step values. My issue is that during rendering, the line created using THREE.js's BufferGeometry and the setDrawRange method, will only be visible if the origin of the line is in the camera's view. Panning away will result in the line disappearing and panning toward the origin of the line (usually 0,0,0) will make it appear again. Is there a reason for this and a way around it? I have tried playing around with render settings.
The code I have included is being used in testing and draws the trace of the object as time progresses.
var traceHandle = {
/* setup() returns trace-line */
setup : function (MAX_POINTS) {
var lineGeo = new THREE.BufferGeometry();
//var MAX_POINTS = 500*10;
var positions = new Float32Array( MAX_POINTS * 3 ); // 3 vertices per point
lineGeo.addAttribute('position', new THREE.BufferAttribute(positions, 3));
var lineMaterial = new THREE.LineBasicMaterial({color:0x00ff00 });
var traceLine = new THREE.Line(lineGeo, lineMaterial);
scene.add(traceLine);
return traceLine;
},
/****
* updateTrace() updates and draws trace line
* Need 'index' saved globally for this
****/
updateTrace : function (traceLine, obj, timeStep, index) {
traceLine.geometry.setDrawRange( 0, timeStep );
traceLine.geometry.dynamic = true;
var positions = traceLine.geometry.attributes.position.array;
positions[index++]=obj.position.x;
positions[index++]=obj.position.y;
positions[index++]=obj.position.z;
// required after the first render
traceLine.geometry.attributes.position.needsUpdate = true;
return index;
}
};
Thanks a lot!
Likely, the bounding sphere is not defined or has radius zero. Since you are adding points dynamically, you can set:
traceLine.frustumCulled = false;
The other option is to make sure the bounding sphere is current, but given your use case, that seems too computationally expensive.
three.js r.73
I want to tween a 3D line using THREE.JS and TweenLite. But the approach that works well with e.g. the position of a sphere does not work out here. I do not know why.
// add a line to the scene using THREE.js
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0, 0, 0));
geometry.vertices.push(new THREE.Vector3(500, 500, 500));
var line = new THREE.Line(geometry, new THREE.LineBasicMaterial());
scene.add( line );
// using TweenLite to animate
var tl = new TimelineLite();
var target = { x: 0, y: 0, z:0 };
line.geometry.verticesNeedUpdate = true;
tl.add(TweenLite.to(line.geometry.vertices[1] , 1, target));
tl.play();
Result: Nothing happens. Why?
PS. The reason might be explained in this post, but I do not understand it.
Found the solution myself: Above the vertex is flagged as needing an update, which happens once in the line line.geometry.verticesNeedUpdate = true;. But this flag needs to be set after each change in the vertex. This can be achieved by putting the update line in the onUpdate function. Now, the line will be called after each update of the vertex.
target.onUpdate = function () {
line.geometry.verticesNeedUpdate = true;
};
In three.js, I want to add a mesh to a position in the scene
I've tried:
// mesh is an instance of THREE.Mesh
// scene is an instance of THREE.Scene
scene.add(mesh)
scene.updateMatrixWorld(true)
mesh.matrixWorld.setPosition(new THREE.Vector3(100, 100, 100))
scene.updateMatrix()
BUT it didn't affect anything.
What should I do ?
I would recommend you to check the documentation over here:
http://threejs.org/docs/#Reference/Objects/Mesh
As you can see on the top of the docu-page, Mesh inherits from "Object3D". That means that you can use all methods or properties that are provided by Object3D. So click on the "Object3D" link on the docu-page and check the properties list. You will find the property ".position". Click on ".position" to see what data-type it is. Paha..its Vector3.
So try to do the following:
// scene is an instance of THREE.Scene
scene.add(mesh);
mesh.position.set(100, 100, 100);
i saw it on a github earlier. (three.js r71 )
mesh.position.set(100, 100, 100);
and can be done for individuals
mesh.position.setX(200);
mesh.position.setZ(200);
reference: https://threejs.org/docs/#api/math/Vector3
detailed explanation is below:
since mesh.position is "Vector3". Vector3() has setX() setY() and setZ() methods. we can use it like this.
mesh.position = new THREE.Vector3() ; //see position is Vector3()
vector1 = new THREE.Vector3();
mesh.position.setX(100); //or this
vector1.setX(100) // because all of them is Vector3()
camera1.position.setZ(100); // or this
light1.position.setY(100) // applicable to any object.position
I prefer to use Vector3 to set position.
let group = new THREE.Group();
// position of box
let vector = new THREE.Vector3(10, 10, 10);
// add wooden Box
let woodenBox = new THREE.Mesh(boxGeometry, woodMaterial);
//update postion
woodenBox.position.copy(vector);
// add to scene
group.add(woodenBox)
this.scene.add(group);
If some one looking for way to update position from Vector3
const V3 = new THREE.Vector3(0,0,0) // Create variable in zero position
const box = new THREE.Mesh(geometry, material) // Create an object
Object.assign(box.position, V3) // Put the object in zero position
OR
const V3 = new THREE.Vector3(0,0,0) // Create variable in zero position
const box = new THREE.Mesh(geometry, material) // Create an object
box.position.copy(V3)