adding and updating velocity and acceleration in bufergeometry three js - javascript

let pointsM = [];
let img = new THREE.TextureLoader().load( 'image' );
let pointMaterial = new THREE.PointsMaterial({
map: img,transparent:true
});
let bufferGeometry = new THREE.BufferGeometry()
for (let i = 0; i < 10; i++) {
pointsM.push( new THREE.Vector3(Math.random() * 20 - 10, Math.random() * 20 - 10, Math.random() * 20 - 10));
}
bufferGeometry.setFromPoints(pointsM);
bufferGeometry.computeVertexNormals();
pointsmesh = new THREE.Points(bufferGeometry, pointMaterial);
scene.add(pointsmesh)
The above section works fine ,
converted from Geometry to BufferGeometry
What id like to do is add velocity and acceleration to the points created in the above section, and update them in the render loop
i would like to change the code below to work with buffer geometry currently it works with Geometry
render(){
Geo.vertices.forEach(p => {
p.vel += p.accel;
p.z -= p.vel;
});
Geo.verticesNeedUpdate = true;
}
Thanks for the support

Try it with the below code. It assumes that the velocity and acceleration for each vertex is saved in two separate arrays.
// creation
const bufferGeometry = new THREE.BufferGeometry();
const vertices = [];
const velocities = [];
const accelerations = [];
for ( let i = 0; i < 10; i++ ) {
vertices.push( Math.random() * 20 - 10 ); // x
vertices.push( Math.random() * 20 - 10 ); // y
vertices.push( Math.random() * 20 - 10 ); // z
velocities.push( 0 );
accelerations.push( Math.random() );
}
bufferGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
// update
const positionAttribute = geometry.getAttribute( 'position' );
for ( let i = 0; i < positionAttribute.count; i ++ ) {
const z = positionAttribute.getZ( i );
const vel = velocities[ i ];
const accel = accelerations[ i ];
vel *= accel;
velocities[ i ] = vel;
z -= vel;
positionAttribute.setZ( i, z );
}
positionAttribute.needsUpdate = true;
render();
BTW: computeVertexNormals() is not necessary in your use case. Points and lines to not need vertex normals.

Related

Three.js: moving object instances from one place to another

I have an object with multiple instances in my Three.js game:
const dummy = new THREE.Object3D();
const OBJECT_FILE_PATH = 'somefile.glb';
let objectGeometry,
objectMaterial,
objectMesh,
objectInstanceNumber = 150;
loader.load(
OBJECT_FILE_PATH,
function(gltf) {
const _objectMesh = gltf.scene.getObjectByName( 'objectName' );
objectGeometry = _objectMesh.geometry.clone();
const defaultTransform = new THREE.Matrix4()
.multiply( new THREE.Matrix4().makeScale( 1, 1, 1 ) );
objectGeometry.applyMatrix4( defaultTransform );
objectMaterial = _objectMesh.material;
objectMesh = new THREE.InstancedMesh( objectGeometry, objectMaterial, objectInstanceNumber );
objectMesh.castShadow = true;
objectMesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage );
scene.add(objectMesh);
var zPos = -90;
var xPos = -90;
var gCounter = 0;
while(zPos <= 90) {
zPos += 3 + Math.floor(Math.random() * 6);
for ( let k = -1; k < 2; k++){
dummy.scale.setScalar( 0.2 + Math.floor(Math.random() * 4) / 10 );//setting random size 40-100%
dummy.rotation.y = Math.random() * Math.PI * 2;//random rotation
xPos = 60 * k - 30 + Math.floor(Math.random() * 60);//cell size: 60x60
dummy.position.set(
xPos,
0,
zPos + Math.floor(Math.random() * 5) / 10
);
dummy.updateMatrix();
objectMesh.setMatrixAt( gCounter, dummy.matrix );
gCounter++;
}
}
},
undefined, // We don't need this function
function(error) {
console.error(error);
}
);
I've placed all the objects where they should be within 9 hypothetical cells (each 60 x 60 unuts big) during initialization. But now, when a character moves, I need to move some objects from cells that are not visible anymore to new empty cells. How do I access those instances. What I need is to loop through the instances and move instances that have coordinates that I need. Something like
for(var i = 0; i < objectInstanceNumber; i++ ){
if(myInstances[i].position.x > -90 && myInstances[i].position.x < -30){
myInstances[i].position.x = 30 + Math.random() * 60;
}
}
Where myInstances would be an array of instances. Can anyone tell me how to do that, please? Thanks in advance.

How do I target children of a "for" loop which creates a mesh, outside of the loop

I'm trying to target every mesh created by one loop inside an another.
I have a loop inside another loop, a first that creates these spheres radially, inside another repeating these rows by incrementing the number of spheres each time the circle of sphere is growing.
I tried to increment a variable to have a continual number (1,2,3,4,5…) even if the loop start again, because the idea is to add each sound of my php array to each mesh that created, but I don't know if I need to do it inside the loop, or how to do it outside the loop?
var mathpi = Math.PI/180;
var startRadians = startAngle + mathpi;
var arraysamples = <?php echo json_encode($samples);?>;
var rangee = Math.round((Object.keys(arraysamples).length) / 5);
var incrementAngle = 360/totalson;
var incrementRadians = incrementAngle * mathpi;
var totalson = 5;
var yp = 30;
for ( var n = 0; n < rangee; n ++) {
for ( var i = 0; i < totalson; i ++ ) {
var object = new THREE.Mesh( sphere, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
var xp = centerX + Math.sin(startRadians) * radius;
var zp = centerZ + Math.cos(startRadians) * radius;
object.position.z = xp;
object.position.x = zp;
object.position.y = yp;
nomsample ++;
scene.add( object );
startRadians += incrementRadians;
}
totalson = totalson + 3;
var nomsample = totalson + 2;
radius = radius + 100;
startAngle = startAngle + 150;
var startRadians = startAngle + mathpi;
var incrementAngle = 360/totalson;
var incrementRadians = incrementAngle * mathpi;
if (yp > 59) {
yp = yp - 30;
} else {
yp = yp + 30;
}
}
and the idea is to have something like :
for ( var i = 0; i < arraysamples.length ; i ++ ) {
var sound[i] = new THREE.PositionalAudio(listener);
audioLoader.load('sounds/'arraysamples[1]'', function (buffer) {
sound[i].setBuffer(buffer);
sound[i].setRefDistance(20);
sound[i].play();
sound[i].setLoop(true);
sound[i].setVolume(1);
});
object.add(sound[i]);
}
I just expect to add every sound of the array distribute to one mesh, with no several mesh with the same sound. I really hope I'm clear.
Thank you for taking time to read my problem and I apologize in advance if the problem seems simple.

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;
}

Threejs Multiple Mesh animation

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();

How to add faces to THREE.BufferGeometry?

I have created programmatically a simple mesh:
var CreateSimpleMesh = new function () {
var xy = [],
maxX = 7,
maxY = 10,
river = [[0, 5], [0, 4], [1, 3], [2, 2], [3, 2], [4, 1], [5, 1], [6, 0]],
grassGeometry = new THREE.BufferGeometry(),
grassVertexPositions = []
this.init = function () {
for (i = 0; i < maxX; i++) {
for (j = 0; j < maxY; j++) {
xy.push([i, j])
}
}
for (var i = 0; i < xy.length; i++) {
grassVertexPositions.push([xy[i][0], xy[i][1], 0])
grassVertexPositions.push([xy[i][0] + 1, xy[i][1], 0])
grassVertexPositions.push([xy[i][0], xy[i][1] + 1, 0])
grassVertexPositions.push([xy[i][0] + 1, xy[i][1] + 1, 0])
grassVertexPositions.push([xy[i][0], xy[i][1] + 1, 0])
grassVertexPositions.push([xy[i][0] + 1, xy[i][1], 0])
}
for (var i = 0; i < grassVertexPositions.length; i++) {
for (var j = 0; j < river.length; j++) {
if (river[j][0] == grassVertexPositions[i][0] && river[j][1] == grassVertexPositions[i][1]) {
grassVertexPositions[i][2] = -0.5
}
}
}
var grassVertices = new Float32Array(grassVertexPositions.length * 3)
for (var i = 0; i < grassVertexPositions.length; i++) {
grassVertices[i * 3 + 0] = grassVertexPositions[i][0];
grassVertices[i * 3 + 1] = grassVertexPositions[i][1];
grassVertices[i * 3 + 2] = grassVertexPositions[i][2];
}
grassGeometry.addAttribute('position', new THREE.BufferAttribute(grassVertices, 3))
var grassMaterial = new THREE.MeshLambertMaterial({color: 0x00ff00}),
grassMesh = new THREE.Mesh(grassGeometry, grassMaterial)
grassMesh.rotation.x = -Math.PI / 2
Test.getScene().add(grassMesh);
}
}
Problem is that this mesh has only vertices. I have tried to add to it faces like in this question using THREE.Shape.Utils.triangulateShape but BufferGeometry is different than normal geometry and it does not work. Is it possible to add faces to BufferGeometry?
EDIT:
Working fiddle
Here is how to create a mesh having BufferGeometry. This is the simpler "non-indexed" BufferGeometry where vertices are not shared.
// non-indexed buffer geometry
var geometry = new THREE.BufferGeometry();
// number of triangles
var NUM_TRIANGLES = 10;
// attributes
var positions = new Float32Array( NUM_TRIANGLES * 3 * 3 );
var normals = new Float32Array( NUM_TRIANGLES * 3 * 3 );
var colors = new Float32Array( NUM_TRIANGLES * 3 * 3 );
var uvs = new Float32Array( NUM_TRIANGLES * 3 * 2 );
var color = new THREE.Color();
var scale = 15;
var size = 5;
var x, y, z;
for ( var i = 0, l = NUM_TRIANGLES * 3; i < l; i ++ ) {
if ( i % 3 === 0 ) {
x = ( Math.random() - 0.5 ) * scale;
y = ( Math.random() - 0.5 ) * scale;
z = ( Math.random() - 0.5 ) * scale;
} else {
x = x + size * ( Math.random() - 0.5 );
y = y + size * ( Math.random() - 0.5 );
z = z + size * ( Math.random() - 0.5 );
}
var index = 3 * i;
// positions
positions[ index ] = x;
positions[ index + 1 ] = y;
positions[ index + 2 ] = z;
//normals -- we will set normals later
// colors
color.setHSL( i / l, 1.0, 0.5 );
colors[ index ] = color.r;
colors[ index + 1 ] = color.g;
colors[ index + 2 ] = color.b;
// uvs
uvs[ index ] = Math.random(); // just something...
uvs[ index + 1 ] = Math.random();
}
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
// optional
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
// set the normals
geometry.computeVertexNormals(); // computed vertex normals are orthogonal to the face for non-indexed BufferGeometry
See the three.js examples for many additional examples of creating BufferGeometry. Also check out the source code for PlaneGeometry and SphereGeometry, which are reasonably easy to understand.
three.js r.143
You can add faces using three.js internal function- fromBufferGeometry. In your case it would be something like this.
var directGeo = new THREE.Geometry();
directGeo.fromBufferGeometry(grassGeometry);
Then use directGeo to build your mesh, and it will have faces.

Categories

Resources