Connecting two components in Three.js - three.js

I would like to build something that allows the connection of two components, kind of like a guitar cable will plug into an amp from a guitar. So I want to connect to one point, and then drag the connection to a second point and have there be some natural hang in the rope. I made this example from a previous SO question (Three.js rope / cable effect - animating thick lines), but I can't seem to get the calculation right. So the question would be, how would I be able to make a function with this signature:
function drawSpline(startPoint, endPoint, ropeLength) {
// returns a new THREE.Line object with a natural curvature for "slack" in the rope between the two points
}
This is what I have so far:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
scene.add(camera);
camera.position.z = 10;
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
const RADIUS = 1;
const SEGMENTS = 16;
const RINGS = 16;
const sphereMaterial =
new THREE.MeshLambertMaterial(
{
color: 0xCC0000
});
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(
RADIUS,
SEGMENTS,
RINGS),
sphereMaterial);
sphere.position.z = 0;
const pointLight =
new THREE.PointLight(0xFFFFFF);
// set its position
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
// add to the scene
scene.add(pointLight);
const shiftRatio = 7.5;
scene.add(drawSpline({x: 0, y: -3, z: 0}, {x: 6, y: 1, z: 0}, 'blue'));
function drawSpline(beginning, end, clr){
let ySign = Math.sign((end.y + beginning.y) / 2)
let appliedRatio = shiftRatio;
let midVector = new THREE.Vector3( (end.x + beginning.x) / 8, (end.y+beginning.y)/2, (end.z+beginning.z)/ 2 )
let positionVector = new THREE.Vector3(0,end.y-beginning.y,end.z-beginning.z)
let orthogVector = new THREE.Vector3(0,positionVector.z,-positionVector.y).normalize()
var curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( beginning.x, beginning.y, beginning.z ),
midVector.clone().addScaledVector(orthogVector,ySign*appliedRatio),
new THREE.Vector3( end.x, end.y, end.z ),
]);
var points = curve.getPoints( 20 );
console.log(points);
var geometry = (new THREE.BufferGeometry()).setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : clr } );
// Create the final object to add to the scene
var curveObject = new THREE.Line( geometry, material );
return curveObject;
}

Related

Three.js bloom layer issue

I already tried my best to find a solution to my problem but without any luck, hope for your help with this.
In my three.js file I have a moon that is surrounded by stars.
The moon is a simple sphere geometry and the stars are a particle system that use a texture. However they just look like flat circles flying around.
So far so good, the particles were flying all around the moon, some in front of it and some behind it. Exactly what I want.
Then I added a bloom effect and worked with layers. So the moon (layer 0) has no bloom and the particles (layer 1) has bloom added to it.
However that way all the star particles are flying behind the moon and not around it anymore.
Does anyone know how to fix this?
Thanks a ton in advance!
Preview video gif-file of the issue
Here is the js code:
const canvas = document.querySelector("canvas.webgl");
const scene = new THREE.Scene();
const sizes = {
width: window.innerWidth,
height: window.innerHeight,
};
const camera = new THREE.PerspectiveCamera(
45,
sizes.width / sizes.height,
0.1,
1000
);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 5;
camera.layers.enable(1);
scene.add(camera);
const params = {
exposure: 1,
bloomStrength: 2,
bloomThreshold: 0,
bloomRadius: 0,
};
let composer, mixer;
const light = new THREE.AmbientLight(0xffffff, 1);
scene.add(light);
const sgeometry = new THREE.SphereGeometry(15, 32, 16);
const smaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
const ssphere = new THREE.Mesh(sgeometry, smaterial);
const loader1 = new THREE.TextureLoader();
const circle = loader1.load("a.png");
const particlesGeometry = new THREE.BufferGeometry();
const particlesCnt = 3000;
const posArray = new Float32Array(particlesCnt * 3);
for (i = 0; i < particlesCnt * 3; i++) {
posArray[i] = (Math.random() - 0.5) * (Math.random() * 20);
}
particlesGeometry.setAttribute(
"position",
new THREE.BufferAttribute(posArray, 3)
);
const mat = new THREE.PointsMaterial({
size: 0.05,
map: circle,
transparent: true,
opacity: 1,
alpha: 0.8,
alphaTest: 0.9,
alphaToCoverage: 0.91,
Blending: THREE.AdditiveBlending,
});
const particlesMesh = new THREE.Points(particlesGeometry, mat);
particlesMesh.position.set(0, 0, -1);
particlesMesh.layers.set(1);
scene.add(particlesMesh);
const geometry = new THREE.SphereBufferGeometry(1, 64, 64);
const material = new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture("moon.jpg"),
side: THREE.DoubleSide,
shininess: 0,
opacity: 1,
transparent: true,
});
const sphere = new THREE.Mesh(geometry, material);
sphere.rotation.set(0, -2.7, 0);
sphere.layers.set(0);
scene.add(sphere);
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
alpha: true,
antialias: true,
});
renderer.autoClear = false;
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.toneMapping = THREE.LinearToneMapping;
const renderScene = new THREE.RenderPass(scene, camera);
const effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
effectFXAA.uniforms.resolution.value.set(
1 / window.innerWidth,
1 / window.innerHeight
);
const bloomPass = new THREE.UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
composer = new THREE.EffectComposer(renderer);
composer.addPass(renderScene);
composer.addPass(effectFXAA);
composer.addPass(bloomPass);
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.toneMappingExposure = Math.pow(0.9, 4.0);
window.addEventListener("resize", () => {
sizes.width = window.innerWidth;
sizes.height = window.innerHeight;
camera.aspect = sizes.width / sizes.height;
camera.updateProjectionMatrix();
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
const clock = new THREE.Clock();
const tick = () => {
window.requestAnimationFrame(tick);
const deltaTime = clock.getDelta();
const elapsedTime = clock.getElapsedTime();
sphere.rotation.y = 0.08 * elapsedTime;
particlesMesh.rotation.y = 0.08 * elapsedTime;
renderer.clear();
camera.layers.set(1);
composer.render();
renderer.clearDepth();
camera.layers.set(0);
renderer.render(scene, camera);
};
tick();
The problem is probably that layer 0 (with the moon) is rendered AFTER layer 1 (with the stars). This makes the renderer paint the moon over the stars.
Try to change the order of the rendering so that layer 0 is first. Keep in mind that layer 1 must render in the composer, and layer 0 with the normal renderer. I think the code will be something like this:
renderer.clear();
camera.layers.set(0);
renderer.render(scene, camera);
renderer.clearDepth();
camera.layers.set(1);
composer.render();

How to animate a threeJS object using GSAP?

I have been learning threeJS just recently and can't get passed a problem. I tried to animate a extruded triangle using the GSAP library. It is just a simple animation to have the triangle move to the right but it seems I did something wrong. Any help is much appreciated.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create Triangles
var material = new THREE.MeshPhongMaterial({
color: 0xf6c12a,
shininess: 70
});
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(2, 3);
shape.lineTo(4, 0);
shape.lineTo(0, 0);
var extrudeSettings = {
steps: 5,
depth: 1,
bevelEnabled: true,
bevelThickness: 0.3,
bevelSize: 0.5,
bevelOffset: 0,
bevelSegments: 1
};
var geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
// Sets the origin to the center of geometry for rotation
geometry.center();
var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = -5;
mesh.scale.set(1.5, 1.5, 1.5);
scene.add(mesh);
gsap.to(mesh, { duration: 2, x: 300 });
camera.position.z = 5;
// Background
var geometry = new THREE.PlaneGeometry(1000, 1000, 1);
var material = new THREE.MeshPhysicalMaterial({ color: 0x444444 });
var plane = new THREE.Mesh(geometry, material);
plane.position.z = -50;
scene.add(plane);
// Lighting
var ambientLight = new THREE.AmbientLight(0xffffff, 0.55);
scene.add(ambientLight);
var pointLight1 = new THREE.PointLight(0xf9eac8, 1, 100);
pointLight1.position.set(5, 10, 0);
pointLight1.castShadow = true;
pointLight1.shadow.camera.top = 20;
scene.add(pointLight1);
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
Here is the link to Codepen
Tried to put the gsap code into different position but maybe that's not the problem.
gsap.to(mesh.position, { duration: 2, x: 300 });
the value that you want to change is mesh.position.x not mesh.x
just add .position it will work

Three.JS VRAM memory leak when adding removing THREE.Geometry to scene

I have encountered a VRAM memory leak in my app.
The app adds and removes THREE.Geometry very often to create a volumetric animation.
If instead of a THREE.Geometry with it's own populated vertices, I used instead THREE.SphereBufferGeometry, the memory leak doesn't happen.
I have created a minimal app to prove this memory leak is real.
The memory leak increase VRAM memory very slowly, but it does fill up in the end.
I think that pools won't help, since it's VRAM and not managed memory.
I do use dispose.
If you can make this sample work and not have memory leak, that might solve my issue:
https://jsfiddle.net/4a7ksryd/16/
Edit: I am adding here the code of the app:
var camera, scene, renderer;
var geometry, material, mesh;
var lastSphere;
var lastGeo;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
var light = new THREE.DirectionalLight( 0xffffff, 1, 100 );
light.position.set( 0, 4, 0 ); //default; light shining from top
light.castShadow = true; // default false
//Set up shadow properties for the light
light.shadow.mapSize.width = 1024; // default
light.shadow.mapSize.height = 1024; // default
light.shadow.camera.near = 1; // default
light.shadow.camera.far = 20; // default
scene.add( light );
//Create a sphere that cast shadows (but does not receive them)
geometry = new THREE.SphereBufferGeometry( 0.1, 32, 32 );
material = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
// geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
// material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
mesh.position.y = 0.1;
mesh.castShadows = true;
mesh.receiveShadow = false;
scene.add( mesh );
var planeGeometry = new THREE.PlaneBufferGeometry( 15, 15, 1, 1 );
var planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff, emissive:0x111111 } )
var plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = -0.2;
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true;
scene.add( plane );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
var dim = 32;
var geo1 = new THREE.Geometry();
const numVertices = dim*dim;
var vertices = new Array(numVertices);
for (var i=0; i<vertices.length; i++)
{
const x = i%dim;
const y = (Math.floor(i/dim))%dim;
vertices[i] = new THREE.Vector3(x*0.1, y*0.1, 0);
}
const numFaces = (dim-1)*(dim-1)*2;
var faces = new Array(numFaces);
for (var i=0; i<(faces.length/2); i++)
{
const x = i%(dim-1);
const y = Math.floor(i/(dim-1))%(dim-1);
faces[2*i] = new THREE.Face3(x+y*dim, x+1+y*dim, x+(y+1)*dim);
faces[2*i+1] = new THREE.Face3(x+1+y*dim, x+1+(y+1)*dim, x+(y+1)*dim);
}
var uv = new Array(numFaces);
for (var i=0; i<uv.length; i++)
uv[i] = [new THREE.Vector2(0, 0), new THREE.Vector2(0, 0), new THREE.Vector2(0, 0)];
geo1.faces = faces;
geo1.vertices = vertices;
geo1.faceVertexUvs[0] = uv;
geo1.uvsNeedUpdate = true;
geo1.verticesNeedUpdate = true;
geo1.elementsNeedUpdate = true;
// var sphereGeometry = new THREE.SphereBufferGeometry( 0.1, 256, 256 );
var sphereGeometry = geo1;
var sphereMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.position.y = 0.1+Math.sin(mesh.rotation.x)*0.1;
sphere.position.x = 0.5;
sphere.castShadow = true; //default is false
sphere.receiveShadow = false; //default
if (lastGeo!=null)
lastGeo.dispose();
if (lastSphere!=null)
scene.remove(lastSphere);
scene.add( sphere );
lastSphere = sphere;
lastGeo = sphereGeometry;
// geo1.dispose();
renderer.render( scene, camera );
}
This is actually a bug in three.js. I've filed a PR to fix the issue:
https://github.com/mrdoob/three.js/pull/20479

Create Rope in Three.js using Physics from Ammo.js or Cannon.js

I have been trying to create in three.js a rope hanging from a point using any of the 3D physics libraries (ammo.js, cannon.js), but the only one I have successfully done is with (2D)verlet.js.
I really need and want to create it in 3D because I need two ropes attached to a midpoint so that I can show the instability of the midpoint as a load is applied. Something similar as the attached image.
enter image description here
To be honest, I have not idea how to start, I have some experience with Three.js, but non with ammo.js or cannon.js. So far I have been trying to understand the codes for the examples in these links with not success.
http://schteppe.github.io/cannon.js/demos/constraints.html
https://threejs.org/examples/#webgl_physics_rope
I even tried to make a rope using spring function from cannon.js, but you can see that my example is not success.
Can somebody please help me or guide me into the correct way to begin my task?
This is the Code I began to write using Cannon.js:
function initCannon()
{
world = new CANNON.World();
world.gravity.set(.2,-10,.2);
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;
var mass = 1;
var damping = 1;
// STATIC SPHERE
var sphereShape = new CANNON.Sphere(new CANNON.Vec3(1,1,1));
mass = 0;
sphereBody = new CANNON.Body({ mass: 0 });
sphereBody.addShape(sphereShape);
sphereBody.position.set(.5,8,.5);
world.addBody(sphereBody);
// DINAMIC SPHERE 1
var shape = new CANNON.Sphere(new CANNON.Vec3(1,1,1));
body = new CANNON.Body({ mass: 1 });
body.addShape(shape);
body.angularDamping = damping;
body.position.set(0,8,0);
world.addBody(body);
// DINAMIC SPHERE 2
var shape2 = new CANNON.Sphere(new CANNON.Vec3(1,1,1));
body2 = new CANNON.Body({ mass: 1 });
body2.addShape(shape2);
body2.angularDamping = damping;
body2.position.set(0,8,0);
world.addBody(body2);
// DINAMIC SPHERE 3
var shape3 = new CANNON.Sphere(new CANNON.Vec3(1,1,1));
body3 = new CANNON.Body({ mass: 1 });
body3.addShape(shape3);
body3.angularDamping = damping;
body3.position.set(0,8,0);
world.addBody(body3);
var size = 1;
var rebote = 1;
var spring = new CANNON.Spring(body,sphereBody,{
localAnchorA: new CANNON.Vec3(0,size,0),localAnchorB: new CANNON.Vec3(0,0,0),
restLength : 0, stiffness : 50, damping : rebote, });
var spring2 = new CANNON.Spring(body2, body,{
localAnchorA: new CANNON.Vec3(0,size,0),localAnchorB: new CANNON.Vec3(0,0,0),
restLength : 0, stiffness : 50, damping : rebote, });
var spring3 = new CANNON.Spring(body3, body2,{
localAnchorA: new CANNON.Vec3(0,size,0),localAnchorB: new CANNON.Vec3(0,0,0),
restLength : 0, stiffness : 50, damping : rebote, });
world.addEventListener("postStep",function(event){ spring.applyForce(); });
world.addEventListener("postStep",function(event){ spring2.applyForce(); });
world.addEventListener("postStep",function(event){ spring3.applyForce(); });
}
function initThree()
{
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.y = 3;camera.position.z = 15;
scene.add( camera );
controls = new THREE.TrackballControls( camera );
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
var material_wire = new THREE.MeshBasicMaterial( { color: 0xFFFFFF, wireframe: true } );
var sphere_size = .8;
var segmentos = 5;
geometry_sphere = new THREE.SphereGeometry( sphere_size, segmentos, segmentos );
sphere = new THREE.Mesh( geometry_sphere, material_wire );
scene.add( sphere );
geometry = new THREE.SphereGeometry( sphere_size, segmentos,segmentos ); // RRT DEFINE TAMANO DE CUBE
mesh = new THREE.Mesh( geometry, material_wire );
scene.add( mesh );
mesh2 = new THREE.Mesh( geometry, material_wire );
scene.add( mesh2 );
mesh3 = new THREE.Mesh( geometry, material_wire );
scene.add( mesh3 );
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
updatePhysics();
render();
}
function updatePhysics() {
// Step the physics world
world.step(timeStep);
sphere.position.copy(sphereBody.position);
mesh.position.copy(body.position); // HACE QUE SE VEA
mesh2.position.copy(body2.position);
mesh3.position.copy(body3.position);
}
function render() {
renderer.render( scene, camera );
}

Physijs object goes through concaveMesh

I've a problem that seems to be known: my "bounding" object doesn't collide with "floor" concaveMesh.
I already read that this issue could be caused by an error in scaling concaveMesh together with the model, so I exported my floor model scaled as I need it and after I applied a concaveMesh (as follow) but it doesn't work.
I red this: https://github.com/chandlerprall/Physijs/issues/102 and a lot of other things about this topic (Physijs Load model three.js collisions don't work and a similar) and I made the following code but nothing to do :(
I really don't understand why "bounding" goes through the floor.
Here my code:
Physijs.scripts.worker = './libs/chandlerprall-Physijs-7e3837b/physijs_worker.js';
Physijs.scripts.ammo = './examples/js/ammo.js';
var gravityVector = new THREE.Vector3( 0, -100, 0 );
//renderer
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor(0xffffff, 0);
renderer.setSize( window.innerWidth, window.innerHeight );
//canvas
var canvas = renderer.domElement;
canvas.setAttribute("width", window.innerWidth);
canvas.setAttribute("height", window.innerHeight);
document.body.appendChild( canvas );
var perspectiveCamera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight, 1, 200000);
//scene
var rttScene = new Physijs.Scene();
var bounding = new Physijs.SphereMesh(new THREE.SphereGeometry(100, 100, 100),
Physijs.createMaterial(
new THREE.MeshBasicMaterial({color: '#ff0000'}),
1.0, // friction
0.0 // restitution
),50 //mass
);
bounding.position.set(200,1200,-5000);
loader.load("http://0.0.0.0:8000/Models/Isola/pavimento.js", function( geometry, materials){
var groundMaterial = Physijs.createMaterial(new THREE.MeshFaceMaterial(materials),
0.8, // friction
0.2 // restitution
);
floor = new Physijs.ConcaveMesh(geometry,groundMaterial,0);
floor.name = "pavimento";
rttScene.add(floor);
initScene();
render();
});
function initScene() {
rttScene.setGravity(gravityVector);
rttScene.add(envModel);
rttScene.add(bounding);
bounding.setAngularFactor(new THREE.Vector3(0, 0, 0));
bounding.setCcdMotionThreshold( 0.1 );
bounding.setCcdSweptSphereRadius( 1 );
var ambientLight = new THREE.AmbientLight(0xD9B775 );
rttScene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xc7af81);
directionalLight.target.position.copy( rttScene.position );
directionalLight.position.set(-550,1950,1950).normalize();
directionalLight.intensity = 0.7;
rttScene.add(directionalLight);
perspectiveCamera.position.set(200,1200,-3000);
perspectiveCamera.lookAt(bounding.position);
}
function render() {
requestAnimationFrame(render);
renderer.clear();
rttScene.simulate();
renderer.render(rttScene, perspectiveCamera);
}
I also tried this into render() function:
var originPoint = bounding.position.clone();
var ray = new THREE.Raycaster(originPoint, new THREE.Vector3(0, -1, 0));
var collisionResults = ray.intersectObjects(rttScene.children)
if (collisionResults.length > 0) {
console.log(collisionResults[0].distance);
}
In console i can read the distance between "bounding" and "floor". This should mean that floor exist as a collider but it doesn't stop bounding from falling. Why?

Resources