THREE.js - position particles evenly on objects faces rather than verticies - javascript

Currently I've managed to create a particleCloud with the particles appearing at each vertex of an object I've imported. However I'm trying to get the particles to firstly position on the flat faces of the object rather than the points between them and secondly evenly distribute particles on those faces.
Basically I'm trying to get my 3d object made out of particles
This is what I have so far:
var loader = new THREE.JSONLoader();
loader.load('./resources/model.json', function (geometry, materials) {
var material = new THREE.MeshFaceMaterial(materials);
var model = new THREE.Mesh(geometry, material);
var particleCount = geometry.vertices.length,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointCloudMaterial({
color: 0xFFFFFF,
size: 1
});
for (var p = 0; p < particleCount; p ++) {
particle = model.geometry.vertices[p];
particles.vertices.push(particle);
}
particleSystem = new THREE.PointCloud(particles, pMaterial);
particleSystem.position.set(0, -100, 0)
particleSystem.scale.set(100,100,100)
scene.add(particleSystem);
});
EDIT - 1
I've attached an image to try describe what i currently have and what i'm trying to achieve. Its using the front on a cube as an example. My object will have more sides to it.

EDIT: The previous answer was outdated.
You can now use MeshSurfaceSampler to generate random samples on the surface of a mesh.
The MeshSurfaceSampler.js file is located in the examples/jsm/math directory.
three.js r.128

You have to set the position of each particle individually to build up your 3d object out of particles. Here's an example that makes a cube:
var particles = 500000;
var geometry = new THREE.BufferGeometry();
var positions = new Float32Array( particles * 3 );
var colors = new Float32Array( particles * 3 );
var color = new THREE.Color();
var n = 1000, n2 = n / 2; // particles spread in the cube
for ( var i = 0; i < positions.length; i += 3 ) {
// positions
var x = Math.random() * n - n2;
var y = Math.random() * n - n2;
var z = Math.random() * n - n2;
positions[ i ] = x;
positions[ i + 1 ] = y;
positions[ i + 2 ] = z;
// colors
var vx = ( x / n ) + 0.5;
var vy = ( y / n ) + 0.5;
var vz = ( z / n ) + 0.5;
color.setRGB( vx, vy, vz );
colors[ i ] = color.r;
colors[ i + 1 ] = color.g;
colors[ i + 2 ] = color.b;
}
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
geometry.computeBoundingSphere();
//
var material = new THREE.PointCloudMaterial( { size: 15, vertexColors: THREE.VertexColors } );
particleSystem = new THREE.PointCloud( geometry, material );
scene.add( particleSystem );
source: this threejs example

Related

Three.js: Changing material of one mesh changes all materials in scene

I'm trying to randomly select a mesh in my scene within THREE JS from an array, selecting what appears to be a Mesh within my scene and editing its properties, seems to affect the entire scene, i.e. it changes the colour of all the meshes, however, I haven't in this example merged any polygons. Any ideas on what I'm doing wrong to select a random one>?
var buildingObjs = [];
for ( var i = 0; i < 20; i ++ ) {
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );
var building = new THREE.Mesh( geometry, material );
var geo = new THREE.EdgesGeometry( building.geometry ); // or WireframeGeometry
var mat = new THREE.LineBasicMaterial( { color: 0xcfcfcf } );
var wireframe = new THREE.LineSegments( geo, mat );
building.add( wireframe );
var value = 1 - Math.random() * Math.random();
var color = new THREE.Color().setRGB( value + Math.random() * 0.1, value, value + Math.random() * 0.1 );
var top = color.clone().multiply( light );
var bottom = color.clone().multiply( shadow );
building.position.x = Math.floor( Math.random() * 200 - 100 ) * 10;
building.position.z = Math.floor( Math.random() * 200 - 100 ) * 10;
building.rotation.y = Math.random();
building.scale.x = building.scale.z = Math.random() * Math.random() * Math.random() * Math.random() * 50 + 10;
building.scale.y = ( Math.random() * Math.random() * Math.random() * building.scale.x ) * 8 + 8;
buildingObjs.push(building);
scene.add(building);
}
function randomBuildingSelector()
{
var randomBuilding = buildingObjs[Math.floor(Math.random()*buildingObjs.length)];
return randomBuilding;
}
function startAniLabels() {
var selectedMesh = undefined;
var tt = setInterval(function(){
selectedMesh = randomBuildingSelector();
console.log(selectedMesh);
selectedMesh.material.color.setHex( 0x333333 );
}, 5000);
}
Below fiddle illustrates:
https://jsfiddle.net/60j5z9w3/
You'll see all buildings change colour, whereas I'm expecting only one to change.
The problem stems from your material variable. You're only using a single material for all buildings, so when you change the color of one, you change the color of all. This is a simplified version of your code for clarity:
// Create 1 material
var material = new THREE.MeshPhongMaterial();
for (var i = 0; i < 2000; i++) {
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
// Use that same material on 2000 meshes
var building = new THREE.Mesh( geometry, material );
}
// This is changing the color of the only material,
// effectively changing the color of all meshes
buildingObjs[500].material.color.setHex( 0x333333 );
You would need to create a new material for each mesh if you're planning on changing them individually.

Three.js shape from random points

I have a N number of random points (in this case 20), with a X,Y and Z constrains.
How can I create ANY (preferably closed) shape (using Three.js library) , given and starting only from N random points.
There are probably many variants, please share yours.
var program = new Program(reset,step)
program.add('g',false)
function reset() {
scene.clear()
scene.add(new THREE.GridHelper(100,1))
}
function step() {
}
program.startup()
var numpoints = 20;
var dots = []; //If you want to use for other task
for (var i = 0 ; i < numpoints ; 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
var dotGeometry = new THREE.Geometry();
dots.push(dotGeometry);
dotGeometry.vertices.push(new THREE.Vector3( x, y, z));
var dotMaterial = new THREE.PointsMaterial( { size: 3, sizeAttenuation: false, color: 0xFF0000 } );
var dot = new THREE.Points( dotGeometry, dotMaterial );
scene.add(dot);
}
Triangulation, Voronoi, I don't care, just show me ANY ideas you have, will help me learn a lot!
You can create a polyhedron which is the convex hull of a set of 3D points by using a pattern like so:
var points = [
new THREE.Vector3( 100, 0, 0 ),
new THREE.Vector3( 0, 100, 0 ),
...
new THREE.Vector3( 0, 0, 100 )
];
var geometry = new THREE.ConvexGeometry( points );
var material = new THREE.MeshPhongMaterial( {
color: 0xff0000,
shading: THREE.FlatShading
} );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
You must include the following in your project
<script src="/examples/js/geometries/ConvexGeometry.js"></script>
three.js r.78

Creating shapes based on Sphere vertices ThreeJs

Here's what i am trying to do, i already have created the vertices based on a formula, and connected lines between these vertices using a threejs method, and now i want to create shapes based on these vertices and connect them together to use them as map tiles, my suggestion is to modify the shape's vertices y, x, z axis, but i'm not able to find the right formula for these vertices:
mesh1 = new THREE.Mesh(); // sphere container
mesh2 = new THREE.Mesh(); // sphere container
mesh3 = new THREE.Mesh(); // sphere container
var R = 5.6; // radius
var LON = 32; var LAT = 16; // approximation
var PILAT = Math.PI/LAT;
var PILON = 2 * Math.PI/LON;
var cos1,cos2,sin1,sin2,t1,t2;
var y1,y2,r1,r2,t1,t2;
var plotG = new THREE.PlaneGeometry(0.06, 0.06);
var lineColor = new THREE.LineBasicMaterial({color: 0xaaaaaa});
var geometry = new THREE.Geometry();
var oldLATCounter = 0;
var oldLONCounter = 0;
for (var i=0; i<LAT; i++){
t1 = Math.PI - i*PILAT;
t2 = Math.PI - (i+1)*PILAT;
oldT1 = Math.PI - oldLATCounter*PILAT;
oldT2 = Math.PI - (oldLATCounter+1)*PILAT;
y1 = Math.cos(t1); // 1 latitudes radius y-position;
y2 = Math.cos(t2); // 2 latitudes radius y-position;
oldY1 = Math.cos(oldT1); // 1 latitudes radius y-position;
oldY2 = Math.cos(oldT2); // 2 latitudes radius y-position;
r1 = Math.abs( Math.sin(t1) ); // 1 latitudes radius;
r2 = Math.abs( Math.sin(t2) ); // 2 latitudes radius;
oldR1 = Math.abs( Math.sin(oldT1) ); // 1 latitudes radius;
oldR2 = Math.abs( Math.sin(oldT2) ); // 2 latitudes radius;
for (var j=0; j<LON; j++) // walk longitudes segments
{
t1 = j*PILON;
t2 = (j+1)*PILON;
oldT1 = oldLONCounter*PILON;
oldT2 = (oldLONCounter+1)*PILON;
cos1 = Math.cos(t1);
cos2 = Math.cos(t2);
sin1 = Math.sin(t1);
sin2 = Math.sin(t2);
oldCos1 = Math.cos(oldT1);
oldCos2 = Math.cos(oldT2);
oldSin1 = Math.sin(oldT1);
oldSin2 = Math.sin(oldT2);
geometry.vertices.push(
new THREE.Vector3( r1*cos1, y1, r1*sin1 ),
new THREE.Vector3( r2*cos1, y2, r2*sin1 ),
new THREE.Vector3( r2*cos2, y2, r2*sin2 )
);
geometry.dynamic = true;
var m1 = new THREE.Mesh( plotG );
m1.position.set(r2*cos2, y2, r2*sin2);
// m1.geometry.vertices[0].y = 0;
// m1.geometry.vertices[0].x = 0;
// m1.geometry.vertices[0].z = 0;
// m1.geometry.vertices[1].y = 0;
// m1.geometry.vertices[1].x = (oldR2*oldCos2) - (r2*cos2);
// m1.geometry.vertices[1].z = -(oldR2*oldSin2);
// m1.geometry.vertices[2].y = oldTy2;
// m1.geometry.vertices[2].x = 0;
// m1.geometry.vertices[2].z = 0.1;
// m1.geometry.vertices[3].y = 0;
// m1.geometry.vertices[3].x = 0;
// m1.geometry.vertices[3].z = 0.1;
mesh2.add(m1.clone());
oldLONCounter = j;
}
oldLATCounter = i;
}
mesh2.add( new THREE.Line( geometry, new THREE.LineBasicMaterial({color: 0xaaaaaa}) ) );
scene.add(mesh2);
mesh2.scale.set(R,R,R);
mesh2.position.x = 0;
This is the sphere i'm working on
Have you checked the THREE.Shape Class? It let you draw a shape/polygon based on some given points.
So my suggestion in your case is to loop through the vertices you need and draw a shape with them.
Here you can find out more about THREE.Shape
If you then want to create a geometry with that shape, to then add it to a mesh, then also have a look at THREE.ShapeGeometry

Three.js MeshPhongMaterial doesn't seem to be affected by non-ambient lights

I have a function which graphs a pair of arrays as a double-sided surface. I have been using threejs for a few weeks, but never with a custom shape so I thought that might be related to the issue. I didn't find any internet answers that helped, although I tried many, so here I am.
I have these two pieces of code which are relevant, but the JsFiddle has the whole thing.
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var spotLight = new THREE.DirectionalLight( 0xffffff );
spotLight.position.set( 0, 1, 1 ).normalize();
scene.add(spotLight);
and
function graph(array1, array2)
{
var particleCount = array1.length*array2.length,
particles = new THREE.Geometry(),
pMaterial = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
shading: THREE.SmoothShading
});
// now create the individual particles
for (var p = 0; p < particleCount; p++) {
var row = (p%array1.length),
col = Math.floor(p/array1.length),
pX = 2*row - array1.length,
pY = 2*col - array2.length,
pZ = (array1[row])*(array2[col]),
particle = new THREE.Vector3(pX, pZ, pY);
// add it to the geometry
particles.vertices.push(particle);
if (row != 0 && col != 0)
{
particles.faces.push( new THREE.Face3( col * array1.length + row, col * array1.length + row - 1, (col - 1) * array1.length + row) );
particles.faces.push( new THREE.Face3( (col - 1) * array1.length + row, col * array1.length + row - 1, (col - 1) * array1.length + row - 1) );
}
}
// create the particle system
var particleSystem = new THREE.Mesh(
particles,
pMaterial);
// add it to the scene
scene.add(particleSystem);
onRenderFcts.push(function(){
var angle = Date.now()/10000 * Math.PI;
particleSystem.rotation.y = angle;
})
}
https://jsfiddle.net/6Lp74xdn/1/
I guess I have just run out of ideas on what could be the reason the directional light doesn't illuminate the surface.
After you generated the vertices and faces you need to call computeFaceNormals().
So, just add particles.computeFaceNormals(); at the end of your graph function.

Physijs update object mass

I am trying to work with a three.js environment with multiple objects that change mass when they are clicked on. Unfortunately the documentation hasn't been helpful and I haven't been able to find any examples of the best way to do this.
Here is the portion in which I am adding a random number of objects to the scene.
var random = getRandomArbitrary(4, 50);
for (var i = 0; i <= random; i++) {
var geometry = new THREE.IcosahedronGeometry( 5, 0 );
var physijsMaterial = Physijs.createMaterial(
new THREE.MeshLambertMaterial( { color: 0xffffff, emissive: 0x333333, shading: THREE.FlatShading } ),
0, // high friction
0 // medium restitution /bouciness
);
var smallSphere = new Physijs.SphereMesh(
geometry,
physijsMaterial,
0
);
smallSphere.position.y = getRandomArbitrary(-50, 50);
smallSphere.position.z = getRandomArbitrary(-50, 50);
smallSphere.position.x = getRandomArbitrary(-50, 50);
smallSphere.name = "Crystals";
scene.add(smallSphere);
}
Here is the portion in which I am trying to update the mass of an object.
function generateGravity(event)
{
event.preventDefault();
var vector = new THREE.Vector3();
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.ray.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( scene.children, true );
console.log(intersects);
// intersects.object
length = intersects.length;
for (var x = 0; x < length; x++) {
if (intersects[x].object.name == "Crystals") {
console.log(intersects[x].object.mass);
intersects[x].object._physijs.mass = 50;
// intersects[x].object.remove();
}
}
}
What I'm curious to know is the best approach to updating the mass of an object during an event. Right now this function generateGravity is being called in the "render" method.
I realized this was happening because I placed scene.simulate() after render().

Categories