Threejs Multiple Mesh animation - javascript

I am very new to threejs, and I have created a for loop to draw 400 cylinders. Everything is fine and they are called into the scene and rendered properly. When I go to animate the objects, only one of the 400 appears to rotate. How can I rotate all of the cylinders?
Code:
for( var j = 0; j < 400; j++){
abgeometry2 = new THREE.CylinderGeometry (1, 5, 8, 4);
abmesh2 = new THREE.MeshPhongMaterial({color: 0x3B170B,
wireframe: false });
mesh2 = new THREE.Mesh(abgeometry2, abmesh2);
mesh2.position.x = Math.random() * 400 - 200;
mesh2.position.y = Math.random() * 400 - 200;
mesh2.position.z = Math.random() * 400 - 200;
scene.add( mesh2 );
}
And in the animate I put : mesh2.rotation.z += 0.06;
I know I may be doing something stupid but I'm not quite familiar with threejs.

You're only applying the rotation to the last cylinder since they're all assigned to mesh2 during the loop.
Try something like this:
var numCylinders = 400;
var cylinders = [];
var geo = new THREE.CylinderGeometry (1, 5, 8, 4);
var mesh = new THREE.MeshPhongMaterial({color: 0x3B170B, wireframe: false });
for (var i = 0; i < numCylinders; i++){
var curCylinder = new THREE.Mesh(geo, mesh);
curCylinder.position.x = Math.random() * 400 - 200;
curCylinder.position.y = Math.random() * 400 - 200;
curCylinder.position.z = Math.random() * 400 - 200;
scene.add(curCylinder);
cylinders.push(curCylinder);
}
var render = function () {
requestAnimationFrame( render );
for (var i = 0; i <numCylinders; i++){
cylinders[i].rotation.z += 0.06;
}
renderer.render(scene, camera);
};
render();

Related

Three.js change the pivot point of each particle

I'm trying to rotate each particle along its own axis of rotation rather than rotate them all along one point. I've looked at many similar questions on setting the pivot points, but I'm lost and confused to be honest.
My goal is to animate the particles as if they're falling from top to bottom, similar to this animation.
var camera,
scene,
renderer,
materials = [],
parameters;
var windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2;
var particles = [];
var confetti = {
maxCount: 2000, //set max confetti count
speed: 1, //set the particle animation speed
frameInterval: 30, //the confetti animation frame interval in milliseconds
waveThreshold: 3, //
start: null, //call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
stop: null, //call to stop adding confetti
toggle: null, //call to start or stop the confetti animation depending on whether it's already running
pause: null, //call to freeze confetti animation
resume: null, //call to unfreeze confetti animation
togglePause: null, //call to toggle whether the confetti animation is paused
remove: null, //call to stop the confetti animation and remove all confetti immediately
isPaused: null, //call and returns true or false depending on whether the confetti animation is paused
isRunning: null //call and returns true or false depending on whether the animation is running
};
init();
animate();
window.addEventListener('resize', onWindowResize);
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 20, 900);
camera.position.z = 100;
var geometry = new THREE.BufferGeometry();
var vertices = [];
var textureLoader = new THREE.TextureLoader();
var sprite1 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite2 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite3 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite4 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
var sprite5 = textureLoader.load('https://i.postimg.cc/Rhx8t6CM/confetti1.png');
for (var i = 0; i < confetti.maxCount; i++) {
var x = Math.random() * 2000 - 1000;
var y = Math.random() * 2000 - 1000;
var z = Math.random() * 1000 - 900;
vertices.push(x, y, z);
//particles[i].tilt = Math.random() * 10 - 10;
//particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
//particles[i].tiltAngle = Math.random() * Math.PI;
particles.push({
'tilt': Math.random() * 1 - 1,
'tiltAngle': Math.random() * Math.PI
})
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
parameters = [
[sprite2, 10],
[sprite3, 9],
[sprite1, 10],
[sprite5, 8],
[sprite4, 10]
];
for (var i = 0; i < parameters.length; i++) {
var sprite = parameters[i][0];
var size = parameters[i][1];
materials[i] = new THREE.PointsMaterial({
size: size,
map: sprite,
blending: THREE.AdditiveBlending,
depthTest: false,
transparent: true
});
var particle = new THREE.Points(geometry, materials[i]);
particle.rotation.x = Math.random() * 5;
particle.rotation.y = Math.random() * 5;
particle.rotation.z = Math.random() * 5;
scene.add(particle);
}
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor('#121212')
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
var time = Date.now() * 0.00005;
for (var i = 0; i < scene.children.length; i++) {
var object = scene.children[i];
if (object instanceof THREE.Points) {
//object.rotation.x = time * ( i < 4 ? i + 1 : ( i + 1 ) );
//object.position.x -= time * ( i < 4 ? i + 1 : -( i + 1 ) );
object.position.y -= 1;
object.position.x += particles[i]['tilt'] / 3;
object.rotation.y += .001;
//object.rotation.x += time * particles[i]['tiltAngle'];
}
}
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>

.points opacity / size within three.js

I'm back for question two on .points. This time wondering how to change the opacity from 0, to 1 and then back within certain pixel distances from the emitter.
var particleCount = 14,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointsMaterial({
map: new THREE.TextureLoader().load("x.png"),
blending: THREE.multiplyBlending,
flatShading: true,
size: 40,
transparent: true,
depthTest: true,
sizeAttenuation: true,
opacity: 1
});
var particleSystem;
My main confusion is that even though I've given it transparency I can't change the value within the update comp I've made for my emitter.
function update() {
//particleSystem.rotation.y += 0.01;
pCount = particleCount;
while (pCount--) {
particle = particles.vertices[pCount];
(This is where a bunch of validation is for where the points are)
particleSystem.geometry.verticesNeedUpdate = true;
particleSystem.rotation.y += (Math.random()*0.001)
}
Render loop:
renderer.setAnimationLoop(() => {
update();
composer.render(scene, camera);
});
I want to make it fade out and not appear in the scene for 20 or so pixels and then fade in. But I'm not entirely sure on how to change the opacity as particle.opacity += 0.1 won't work.
Edit: I'm also uncertain about Size as I want to do a similar thing with it but from 20 to 40, I could probably base it depending on it's Y cordinate. Anyway; I'm also uncertain how to gradually change that too.
Sorry if this is a obvious answer, duplicate question and any help I get. Any alternate methods of what I've seen is in an alternate structure that I don't understand or in array in which I don't know how to put into what I want.
(Thanks in advance)
The issue is that the opacity and the size is a property of the THREE.PointsMaterial. If the pints should have different sizes it is not sufficient to have a list of different vertices in one THREE.Points. There has to be a list of different THREE.Points with different HREE.PointsMaterials.
Create a list of THREE.Points with different materials:
var texture = new THREE.TextureLoader().load( "..." );
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
// ...
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
So in update the opacity and size can be changed individually:
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
// ....
if ( material.size < 40 )
material.size += 0.5;
if ( material.opacity < 1 )
material.opacity += 0.01;
// ....
}
}
var canvas_w = window.innerWidth, canvas_h = window.innerHeight;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, canvas_w/canvas_h, 1, 1000);
camera.position.set(0, 0, 400);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(canvas_w, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.onresize = function() {
canvas_w = window.innerWidth, canvas_h = window.innerHeight;
renderer.setSize(canvas_w, canvas_h);
camera.aspect = canvas_w/canvas_h;
camera.updateProjectionMatrix();
}
var texture = new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/circle.png");
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
var px = (Math.random() - 0.5) * 100;
var py = (Math.random() - 0.5) * 100 + 200;
var pz = (Math.random() - 0.5) * 100;
var particle = new THREE.Vector3(px, py, pz);
particle.velocity = new THREE.Vector3(0, 0, 0);
geometry.vertices.push(particle);
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
if (particle.y < -200) {
particle.x = (Math.random() - 0.5) * 100;
particle.y = (Math.random() - 0.5) * 100 + 200;
particle.z = (Math.random() - 0.5) * 100;
particle.velocity.y = 0;
material.size = 20;
material.opacity = 0;
}
particle.velocity.y -= Math.random() * .1;
particle.add(particle.velocity);
if ( material.size < 40 )
material.size += 0.25;
if ( material.opacity < 1 )
material.opacity += 0.01;
points.geometry.verticesNeedUpdate = true;
points.rotation.y += (Math.random()*0.001)
}
}
renderer.setAnimationLoop(() => {
update();
renderer.render(scene, camera);
});
body { overflow: hidden; margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>

How to detect what side of a cube is clicked

Right now, I am trying to make a navigational menu, but to do this, I need to detect what side is clicked by the user. Is there any way to do this with raycasting, or if not, any other way?
Here is my code if you need it:
CodePen Link
The short version is here
var geometry = new THREE.BoxGeometry(200, 200, 200);
var material = new THREE.MeshLambertMaterial(
{
color: 65535,
morphTargets: true
});
for (var i = 0; i < 8; i++)
{
var vertices = [];
for (var v = 0; v < geometry.vertices.length; v++)
{
vertices.push(geometry.vertices[v].clone());
if (v === i)
{
vertices[vertices.length - 1].x *= 2;
vertices[vertices.length - 1].y *= 2;
vertices[vertices.length - 1].z *= 2
}
}
geometry.morphTargets.push(
{
name: "target" + i,
vertices: vertices
})
}
mesh = new THREE.Mesh(geometry, material);
mesh.position.y = 0;
scene.add(mesh);
scene.background = new THREE.Color(15790320);
var params = {
influence1: 1,
influence2: 1,
influence3: 1,
influence4: 1,
influence5: 1,
influence6: 1,
influence7: 1,
influence8: 1
};
var geometry = new THREE.PlaneBufferGeometry(5e3, 5e3);
geometry.rotateX(-Math.PI / 2);
var material = new THREE.MeshBasicMaterial(
{
color: 975132,
overdraw: .5
});
onMouseDown(event) {
this.mouse.x = (event.pageX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.pageY / window.innerHeight) * 2 + 1;
this.raycaster.setFromCamera(this.mouse, this.camera);
let intersectCube = this.raycaster.intersectObjects( Cube , true );
}
Make a raycaster on your mouse and check for intersections with the Cube or its faces

Planes trimmed by mesh three.js

I have a mesh, which should represent a "building" and I want to add planes every 3 units (different floor levels) and trim them with the mesh faces, so they appear only inside the mesh.
How do I do that? I can't figure it out and the mesh is quite complex for me to define the plane to normally cover only the inside.
var program = new Program(reset, step)
function reset() {
scene.clear()
scene.add(new THREE.GridHelper(100, 1))
}
function step() {
}
program.startup()
// the points group
var spGroup;
// the mesh
var hullMesh;
generatePoints();
render();
function generatePoints() {
// add 10 random spheres
var points = [];
for (var i = 0; i < 50; i++) {
var x = Math.random() * (80 - 1) + 1 //Math.random() * (max - min) + min
var y = Math.random() * (80 - 1) + 1
var z = Math.random() * (80 - 1) + 1
points.push(new THREE.Vector3(x, y, z));
}
spGroup = new THREE.Object3D();
var material = new THREE.MeshBasicMaterial({
color: 0xff0000,
transparent: false
});
points.forEach(function(point) {
var spGeom = new THREE.SphereGeometry(0.5);
var spMesh = new THREE.Mesh(spGeom, material);
spMesh.position.copy(point);
spGroup.add(spMesh);
});
// add the points as a group to the scene
scene.add(spGroup);
// use the same points to create a convexgeometry
var hullGeometry = new THREE.ConvexGeometry(points);
hullMesh = createMesh(hullGeometry);
scene.add(hullMesh);
}
function createMesh(geom) {
// assign two materials
var meshMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
transparent: true,
opacity: 0.2
});
meshMaterial.side = THREE.DoubleSide;
var wireFrameMat = new THREE.MeshBasicMaterial();
wireFrameMat.wireframe = true;
// create a multimaterial
var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial, wireFrameMat]);
return mesh;
}

Three.js render white part of plain geometry

I'm trying to turn this plainGeometry, into this (sort of mask).
This code works: (to sum-up, create two materials, divide the plane to segments and decide for each one which material is it with MeshFaceMaterial)
Button.onClick(function () {
var obj = editor.selected;
var material = obj.material;
var tex = material.map;
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, objWidth, objHeight);
var facesNum = objHeight * objWidth * 2;
var facesX = objWidth * 2;
var facesInX = texWidth * 2;
var materials = [];
materials.push(material);
materials.push(new THREE.MeshBasicMaterial({ }));
for (var i = 0; i < facesNum; i++) {
if ((i % facesX >= objWidth - texWidth) &&
(i % facesX <= (facesInX + objWidth - texWidth - 1)) &&
(i <= (texHeight * objWidth * 2) - 1)) {
geometry.faces[i].materialIndex = 0;
}
else {
geometry.faces[i].materialIndex = 1;
}
}
obj.geometry = geometry;
obj.material = new THREE.MeshFaceMaterial(materials);
editor.signals.materialChanged.dispatch(obj);
});
But I'm wondering if there is a simpler way to go. Any suggestions?
Another way to do this is to use an alpha channel on your texture. You can do this with Gimp or Photoshop.
You then duplicate the mesh and push it just a tad out the axis with polygonOffsetFactor on the material. Apply the background material to the first mesh, and the foreground material with the texture with alpha to the second.
See this fiddle alphaTest. (you may need to disable cross-domain access security so the texture can load in this fiddle, chrome will allow this if you run it with the --disable-web-security flag)
The advantage to this method is that the image can be of any shape and location, and doesn't need to fit into a geometry face.
Another way, if the geometry you were using is complicated, is to use Three.DecalGeometry to cut a mesh piece out and use it shifted a bit with polygonOffsetFactor on the material. See the three.js decals example for this.
Source for the example fiddle is below:
var renderer;
var pointLight;
var scene;
var plane1;
var plane2;
function addPlane()
{
var material1 = new THREE.MeshPhongMaterial({ color: 0xFFFFFF });
var material2;
var loader = new THREE.TextureLoader();
loader.load('http://i.imgur.com/ETdl4De.png',
function ( texture ) {
var material2 = new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
map: texture,
alphaTest: 0.7,
polygonOffset: true,
polygonOffsetFactor: - 4,
});
var geometry1 = new THREE.PlaneGeometry(12, 12, 12, 12);
var geometry2 = geometry1.clone();
plane1 = new THREE.Mesh(geometry1,material1);
plane2 = new THREE.Mesh(geometry2,material2);
scene.add(plane2);
scene.add(plane1);
}
);
}
(function() {
'use strict';
scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10000);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
addPlane();
camera.position.z = 52;
pointLight = new THREE.DirectionalLight(0xffffff,1);
pointLight.position.x = 11;
pointLight.position.y = 5;
pointLight.position.z = 25;
scene.add(pointLight);
var reqAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
var render = function() {
reqAnimFrame(render);
renderer.render(scene, camera);
};
render();
}());
this did it, eventually:
var obj = editor.selected;
var tex = obj.material.map.;
var materials = [];
materials.push(new THREE.MeshBasicMaterial({ map: tex }));
materials.push(new THREE.MeshBasicMaterial({}));
var material = new THREE.MeshFaceMaterial(materials);
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
tex.repeat = new THREE.Vector2(3, 3);
tex.offset = new THREE.Vector2(0, 0);
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, 3, 3);
var v = geometry.vertices;
var facesNum = geometry.faces.length;
v[1] = new THREE.Vector3(-texWidth / 2, objHeight / 2, 0);
v[2] = new THREE.Vector3(texWidth / 2, objHeight / 2, 0);
v[5] = new THREE.Vector3(-texWidth / 2, (objHeight / 2) - texHeight, 0);
v[6] = new THREE.Vector3(texWidth / 2, (objHeight / 2) - texHeight, 0);
v[9] = v[13];
v[10] = v[14];
v[4] = v[8] = v[12];
v[7] = v[11] = v[15];
for (var i = 0; i < facesNum; i++) {
if (i !== 2 && i !== 3) geometry.faces[i].materialIndex = 1;
}
obj.material = material;
obj.geometry = geometry;
editor.signals.materialChanged.dispatch(obj);

Categories

Resources