Cloth Simulaltion of a 3d obj cloth model - three.js

I have seen many cloth simulations in three.js . I found it is done only with 2d plane surfaces . But is there a way that I can simulate a 3d cloth model like the one below ..
There are many tutorials for plane 2d simualtion like
And the code for them is given below...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<script src="../build/three.js"></script>
<script src="../src/OrbitControls.js"></script>
<script>
var params = {
enableWind: true,
tooglePins: togglePins
};
var DAMPING = 0.03;
var DRAG = 1 - DAMPING;
var MASS = 0.1;
var restDistance = 25;
var xSegs = 10;
var ySegs = 10;
var clothFunction = plane(restDistance * xSegs, restDistance * ySegs);
var cloth = new Cloth(xSegs, ySegs);
var GRAVITY = 981 * 1.4;
var gravity = new THREE.Vector3(0, -GRAVITY, 0).multiplyScalar(MASS);
var TIMESTEP = 18 / 1000;
var TIMESTEP_SQ = TIMESTEP * TIMESTEP;
var pins = [];
var windForce = new THREE.Vector3(0, 0, 0);
var tmpForce = new THREE.Vector3();
var lastTime;
function plane(width, height) {
return function(u, v, target) {
var x = (u - 0.5) * width;
var y = (v + 0.5) * height;
var z = 0;
target.set(x, y, z);
};
}
function Particle(x, y, z, mass) {
this.position = new THREE.Vector3();
this.previous = new THREE.Vector3();
this.original = new THREE.Vector3();
this.a = new THREE.Vector3(0, 0, 0); // acceleration
this.mass = mass;
this.invMass = 1 / mass;
this.tmp = new THREE.Vector3();
this.tmp2 = new THREE.Vector3();
// init
clothFunction(x, y, this.position); // position
clothFunction(x, y, this.previous); // previous
clothFunction(x, y, this.original);
}
// Force -> Acceleration
Particle.prototype.addForce = function(force) {
this.a.add(
this.tmp2.copy(force).multiplyScalar(this.invMass)
);
};
// Performs Verlet integration
Particle.prototype.integrate = function(timesq) {
var newPos = this.tmp.subVectors(this.position, this.previous);
newPos.multiplyScalar(DRAG).add(this.position);
newPos.add(this.a.multiplyScalar(timesq));
this.tmp = this.previous;
this.previous = this.position;
this.position = newPos;
this.a.set(0, 0, 0);
};
var diff = new THREE.Vector3();
function satisfyConstraints(p1, p2, distance) {
diff.subVectors(p2.position, p1.position);
var currentDist = diff.length();
if (currentDist === 0) return; // prevents division by 0
var correction = diff.multiplyScalar(1 - distance / currentDist);
var correctionHalf = correction.multiplyScalar(0.5);
p1.position.add(correctionHalf);
p2.position.sub(correctionHalf);
}
function Cloth(w, h) {
w = w || 10;
h = h || 10;
this.w = w;
this.h = h;
var particles = [];
var constraints = [];
var u, v;
// Create particles
for (v = 0; v <= h; v++) {
for (u = 0; u <= w; u++) {
particles.push(
new Particle(u / w, v / h, 0, MASS)
);
}
}
// Structural
for (v = 0; v < h; v++) {
for (u = 0; u < w; u++) {
constraints.push([
particles[index(u, v)],
particles[index(u, v + 1)],
restDistance
]);
constraints.push([
particles[index(u, v)],
particles[index(u + 1, v)],
restDistance
]);
}
}
for (u = w, v = 0; v < h; v++) {
constraints.push([
particles[index(u, v)],
particles[index(u, v + 1)],
restDistance
]);
}
for (v = h, u = 0; u < w; u++) {
constraints.push([
particles[index(u, v)],
particles[index(u + 1, v)],
restDistance
]);
}
this.particles = particles;
this.constraints = constraints;
function index(u, v) {
return u + v * (w + 1);
}
this.index = index;
}
function simulate(time) {
if (!lastTime) {
lastTime = time;
return;
}
var i, j, il, particles, particle, constraints, constraint;
// Aerodynamics forces
if (params.enableWind) {
var indx;
var normal = new THREE.Vector3();
var indices = clothGeometry.index;
var normals = clothGeometry.attributes.normal;
particles = cloth.particles;
for (i = 0, il = indices.count; i < il; i += 3) {
for (j = 0; j < 3; j++) {
indx = indices.getX(i + j);
normal.fromBufferAttribute(normals, indx);
tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce));
particles[indx].addForce(tmpForce);
}
}
}
for (particles = cloth.particles, i = 0, il = particles.length; i < il; i++) {
particle = particles[i];
particle.addForce(gravity);
particle.integrate(TIMESTEP_SQ);
}
// Start Constraints
constraints = cloth.constraints;
il = constraints.length;
for (i = 0; i < il; i++) {
constraint = constraints[i];
satisfyConstraints(constraint[0], constraint[1], constraint[2]);
}
// Floor Constraints
for (particles = cloth.particles, i = 0, il = particles.length; i < il; i++) {
particle = particles[i];
pos = particle.position;
if (pos.y < -250) {
pos.y = -250;
}
}
// Pin Constraints
for (i = 0, il = pins.length; i < il; i++) {
var xy = pins[i];
var p = particles[xy];
p.position.copy(p.original);
p.previous.copy(p.original);
}
}
/* testing cloth simulation */
var pinsFormation = [];
var pins = [6];
pinsFormation.push(pins);
pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
pinsFormation.push(pins);
pins = [0];
pinsFormation.push(pins);
pins = []; // cut the rope ;)
pinsFormation.push(pins);
pins = [0, cloth.w]; // classic 2 pins
pinsFormation.push(pins);
pins = pinsFormation[1];
function togglePins() {
pins = pinsFormation[~~(Math.random() * pinsFormation.length)];
}
var container, stats;
var camera, scene, renderer;
var clothGeometry;
var sphere;
var object;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
scene.fog = new THREE.Fog(0xcce0ff, 500, 10000);
// camera
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(1000, 50, 1500);
// lights
scene.add(new THREE.AmbientLight(0x666666));
var light = new THREE.DirectionalLight(0xdfebff, 1);
light.position.set(50, 200, 100);
light.position.multiplyScalar(1.3);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
var d = 300;
light.shadow.camera.left = -d;
light.shadow.camera.right = d;
light.shadow.camera.top = d;
light.shadow.camera.bottom = -d;
light.shadow.camera.far = 1000;
scene.add(light);
// cloth material
var loader = new THREE.TextureLoader();
var clothTexture = loader.load('textures/water/Water_1_M_Flow.jpg');
clothTexture.anisotropy = 16;
var clothMaterial = new THREE.MeshLambertMaterial({
map: clothTexture,
side: THREE.DoubleSide,
// wireframe: true,
// alphaTest: 0.5
});
// cloth geometry
clothGeometry = new THREE.ParametricBufferGeometry(clothFunction, cloth.w, cloth.h);
// cloth mesh
object = new THREE.Mesh(clothGeometry, clothMaterial);
object.position.set(0, 0, 0);
object.castShadow = true;
scene.add(object);
// object.customDepthMaterial = new THREE.MeshDepthMaterial({
// depthPacking: THREE.RGBADepthPacking,
// map: clothTexture,
// alphaTest: 0.5
// });
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.shadowMap.enabled = true;
// controls
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = Math.PI * 0.5;
controls.minDistance = 1000;
controls.maxDistance = 5000;
window.addEventListener('resize', onWindowResize, false);
}
//
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function animate() {
requestAnimationFrame(animate);
var time = Date.now();
var windStrength = Math.cos(time / 7000) * 20 + 40;
windForce.set(Math.sin(time / 2000), Math.cos(time / 3000), Math.sin(time / 1000));
windForce.normalize();
windForce.multiplyScalar(windStrength);
simulate(time);
render();
}
function render() {
var p = cloth.particles;
for (var i = 0, il = p.length; i < il; i++) {
var v = p[i].position;
clothGeometry.attributes.position.setXYZ(i, v.x, v.y, v.z);
}
clothGeometry.attributes.position.needsUpdate = true;
clothGeometry.computeVertexNormals();
renderer.render(scene, camera);
}
</script>
</body>
</html>
can I make a mesh Just like hanging the cloth and when wind blows they must react accordingly.
Whether by using three.js along with ammo.js or cannon.js too

The code you posted will not do clothing as it as no collisions. The code in ammo.js will but you need to generate the clothing yourself.
Cloth is typically simulated using masses and springs
M--s--M--s--M--s--M
|\ /|\ /|\ /|
| \ / | \ / | \ / |
s s s s s s s
| / \ | / \ | / \ |
|/ \|/ \|/ \|
M--s--M--s--M--s--M
|\ /|\ /|\ /|
| \ / | \ / | \ / |
s s s s s s s
| / \ | / \ | / \ |
|/ \|/ \|/ \|
M--s--M--s--M--s--M
above is a diagram of masses (M) and springs (s). Each spring is connected between 2 masses and tries to keep the masses from stretching too far apart and getting too close as well. You need 1000s of masses and springs to simulate clothing.
The reason the demos are in planes is because it's the easiest demo to make. If you want clothes you need to walk the polygons of your clothing and then generate masses and springs. Further, you need to associate the masses with their corresponding vertices in the clothing model so that after the simulation runs you can apply the masses' new positions back to the vertices of the clothing.
On top of that you need collisions on the body of the character you're going to put the clothing on that the masses will collide with so they don't go inside the body and just fall ot the floor. Most physics engines have a few primitives like box, sphere, capsule, cylinder that are optimzied. They can also use generic polygons for collision but they are slower so it's up to you to decide if you can use a few of the primitive shapes attached to your model to do the collisions or if you need the higher fidelity of using polygons for your collisions.
In either case the more masses you add per area of cloth the better the cloth will look but the slower it will run so you have to decide where the trade off is between looking good and running fast.
ammo.js AFAICT is undocumented except to say it is a port of Bullet Physics who's documentation is here
I don't see any JavaScript demos for custom cloth.
This ammo.js demo
doesn't seem to fit clothing because if those shapes shown were actually clothing they'd just collapse into a pile, not act like they are inflated but then maybe getting that behavior is a setting. You'll need to dig through the docs and/or that sample
You'll need to separate your clothing geometry from the human body / manekin model, turn the clothing into a soft body or manually generate the masses and springs and then also make human/manekin hard body either from a mesh or from primitives so it holds up the clothing.
If I was doing it I'd start with a hard body cube inside a soft body sphere and see how detailed I need to make the sphere for it to behave like clothing (fold and crease)

Popup Dev I have been working with this, but find using a plane geometry to manipulate is much easier. I also have have questions about how the original cloth example calculates and uses 'wind'. Contact me on twitter and lets discuss.

Related

Three.js Move an object to front of camera

Hello I'm trying to move an object to front of camera, and when it reached to target position, I want it to stop. but it doesn't work.
function objectToCamera(mX, mY, object)
{
var vector = new THREE.Vector3(mX, mY, 1);
vector.unproject(camera);
vector.sub(object.position);
var dx = object.position.x - camera.position.x;
var dy = object.position.y - camera.position.y;
var dz = object.position.z - camera.position.z;
var distance = Math.sqrt(dx*dx + dy*dy + dz*dz);
if(lastDistance < distance && lastDistance != -1)
keepOut = -1;
lastDistance = distance;
setTimeout(function(){
if( distance > 200 && keepOut == 1)
{
var amount = (1)*(indexForZoom/3);
amount = (amount>15) ? 15 : (1)*(indexForZoom/3);
if(distance - amount < 200)
amount = (distance-200)+1;
indexForZoom++;
object.translateZ(amount);
controls.target.addVectors(controls.target,vector.setLength(amount));
objectToCamera(mX, mY, object)
}
else
{
// stopForZoom = 1;
keepOut = -1;
objectClickHandler(object.name, object);
}
}, 10);
}
I'm checking the distance between camera and object, and if target distance has reached I'm letting it stop, but it doesn't work.
In coordinates, if i'm in positive X coordinates, distance is decreasing, and otherwise, distance is increasing.
I think, in my codes, distance should be decreasing always, but it is not.
Please help. Thanks.
you can use object.position.lerp(target, amount) to move an object toward target. Amount is a value from 0 to 1 with 1 = 100% all the way to target and 0.5 = 50% way to target.
If you want to move at a fixed speed then you can get the distance to the target
distance = object.position.distanceTo(target);
Say you want a max of 0.1 units per interation. then
moveSpeed = 0.1;
distance = object.position.distanceTo(target);
amount = Math.min(moveSpeed, distance) / distance;
object.position.lerp(target, amount)
All that's left is for you to choose a target.
The position in front of the camera is
const distanceFromCamera = 3; // 3 units
const target = new THREE.Vector3(0, 0, -distanceToCamera);
target.applyMatrix4(camera.matrixWorld);
So for example if you move the camera (drag with mouse, use scrollwheel). Note: in the code the speed is adjusted to be frame rate independent.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 45;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 20);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 0, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightblue');
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(0, 10, 0);
light.target.position.set(-5, 0, 0);
scene.add(light);
scene.add(light.target);
}
const gridHelper = new THREE.GridHelper(100, 10);
scene.add(gridHelper);
gridHelper.position.set(0, -5, 0);
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(1, 1, 1),
new THREE.MeshPhongMaterial({color: 'red'}),
);
scene.add(cube);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
let then = 0;
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then;
then = now;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cube.rotation.x = now;
cube.rotation.y = now * 1.1;
// move cube in front of camera
{
const distanceFromCamera = 3; // 3 units
const target = new THREE.Vector3(0, 0, -distanceFromCamera);
target.applyMatrix4(camera.matrixWorld);
const moveSpeed = 15; // units per second
const distance = cube.position.distanceTo(target);
if (distance > 0) {
const amount = Math.min(moveSpeed * deltaTime, distance) / distance;
cube.position.lerp(target, amount);
cube.material.color.set('green');
} else {
cube.material.color.set('red');
}
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/examples/js/controls/OrbitControls.js"></script>
<canvas id="c"></canvas>
Note, you might want to call camera.updateMatrixWorld() before all that math to make sure the target isn't one frame late.
If the object is in a hierarchy then there's more to do. You can do the math or you can use just attach the object to the scene and then attach it it back to its place in the hierarchy
const parent = object.parent;
// move object to scene without changing it's world orientation
scene.attach(object);
// do stuff above
// move object to parent without changing it's world orientation
parent.attach(object);

THREEJS - Make meshes perpendicular to the Sphere face it's sitting on

// THREEJS RELATED VARIABLES
var scene, camera, renderer, container, controls, raycaster, sphere_radius, HEIGHT, WIDTH;
//INIT THREE JS, SCREEN AND MOUSE EVENTS
function createScene() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
scene = new THREE.Scene();
aspectRatio = WIDTH / HEIGHT;
camera = new THREE.PerspectiveCamera(60, WIDTH / HEIGHT, 1, 10000);
camera.position.x = 0;
camera.position.z = 1500;
camera.position.y = 0;
// ORBIT CAMERA
controls = new THREE.OrbitControls( camera );
controls.update();
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(WIDTH, HEIGHT);
container = document.getElementById('world');
container.appendChild(renderer.domElement);
}
function randomSpherePoint(x0,y0,z0,radius){
var u = Math.random();
var v = Math.random();
var theta = 2 * Math.PI * u;
var phi = Math.acos(2 * v - 1);
var x = x0 + (radius * Math.sin(phi) * Math.cos(theta));
var y = y0 + (radius * Math.sin(phi) * Math.sin(theta));
var z = z0 + (radius * Math.cos(phi));
return [x,y,z];
}
Island = function(){
this.mesh = new THREE.Object3D();
// number of cubes
this.nLands = 30;
this.lands = [];
for(var i = 0; i < this.nLands; i++){
var c = new Land();
this.lands.push(c);
var stepAngle = Math.PI*2 * Math.random();
var a = stepAngle*i;
var h = sphere_radius;
var randPos = randomSpherePoint(0,0,0,sphere_radius);
c.mesh.position.y = randPos[1];
c.mesh.position.x = randPos[0];
c.mesh.position.z = randPos[2];
this.mesh.add(c.mesh);
}
}
Land = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "land";
var geom = new THREE.CylinderGeometry( 2, 2, 50, 64 );
var mat = new THREE.MeshPhongMaterial({color:0x59332e});
var m = new THREE.Mesh(geom.clone(), mat);
m.position.x = 0;
m.position.y = Math.random()*2;
m.position.z = Math.random()*10;
this.mesh.add(m);
}
Sea = function(){
// radius top, radius bottom, height, number of segments on the radius, number of segments vertically
sphere_radius = 300;
var geom = new THREE.SphereGeometry(sphere_radius,sphere_radius,32,32);
var mat = new THREE.MeshNormalMaterial({
transparent:true,
opacity:.5,
flatShading:true,
});
this.mesh = new THREE.Mesh(geom, mat);
this.mesh.name = "sea";
}
// 3D Models
var sea;
function createSea(){
sea = new Sea();
sea.mesh.position.y = 0;
scene.add(sea.mesh);
}
function createIsland(){
island = new Island();
island.mesh.position.y = 0;
scene.add(island.mesh);
}
function loop(){
renderer.render(scene, camera);
requestAnimationFrame(loop);
}
function init(event){
createScene();
createSea();
createIsland();
loop();
}
window.addEventListener('load', init, false);
function handleWindowResize() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
window.addEventListener('resize', handleWindowResize, false);
html, body {
overflow: hidden;
margin:0;
padding:0;
}
<div id="world"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r94/js/controls/OrbitControls.js"></script>
Related codepen:
https://codepen.io/farisk/pen/zLymrz
On three.js, i have a bunch of cylinders added to random positions on the exterior of sphere.
The issue im having now is that they're all facing straight up to the z-axis.
Im thinking that what i should do is to make the cylinder lookAt the angle of where the cylinder intersects the sphere. But im not sure how to achieve this. Any help is appreciated.
Heres a image that describes the situation.

Can not get uniform speed when camera move along the curve

I want to make camera move along the curve,it works, but when pass the turning corner,camera speed changed,looks like slowly.
curve = new THREE.CatmullRomCurve3(vectors);
curve.type = 'catmullrom';
curve.tension = 0.2;
this.MKY.Camera.current = this.roamCamera;
cameraWrap.add(this.roamCamera);
this.MKY.scene.add(cameraWrap);
this.MKY.update.push(roam);
function roam() {
if(!isAutoRoam){return}
if(progress>1 || progress==1){
progress = 0;
return
}
progress += 0.0005;
var position = curve.getPointAt(progress);
position.y += 1.5;
var tangent = curve.getTangentAt(progress);
cameraWrap.position.copy(position);
cameraWrap.lookAt(position.sub(tangent));
};
getPointAt returns a vector for point at a relative position in curve according to arc length. I think if progress not change ,i will get the average speed,but it is not. I do not understend.
There is the approach, using .getUtoTmapping() method of THREE.Curve() (in the example, it's THREE.CatmullRomCurve3).
The documentation says:
.getUtoTmapping ( u, distance )
Given u in the range ( 0 .. 1 ), returns t also in the range ( 0 .. 1 ). u and t can then be used to give you points which are equidistant from the ends of the curve, using .getPoint.
So, when you provide the second parameter in this method, then, if I got it correctly from the source code, it ignores the first parameter, thus you can find the point on your curve by the distance on it.
In the given picture:
small yellow points - points, taken with .getPoints() method;
big maroon points - points, whose distance between each other along the curve is 1 unit;
The code for the maroon points:
var unitPoints = [];
for (let i = 0; i < spline.getLength(); i++){
let p = spline.getUtoTmapping(0, i);
let p1 = spline.getPoint(p);
unitPoints.push(p1);
}
var unitPointsGeometry = new THREE.Geometry();
unitPointsGeometry.vertices = unitPoints;
var units = new THREE.Points(unitPointsGeometry, new THREE.PointsMaterial({size: .125, color: "maroon"}));
scene.add(units);
Look at the source code of the code snippet and pay attention to the getProgress() function.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, .1, 1000);
camera.position.set(0, 1.5, 3);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x181818);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(4, 8));
var spline = new THREE.CatmullRomCurve3(
[
new THREE.Vector3(-2, 0, 0),
new THREE.Vector3(-1.9, .1, .1),
new THREE.Vector3(1, 1, 1),
new THREE.Vector3(0, -1, -2),
new THREE.Vector3(2, 0, 1)
]
);
spline.closed = true;
var splinePoints = spline.getPoints(200);
var lineGeom = new THREE.Geometry();
lineGeom.vertices = splinePoints;
var line = new THREE.Line(lineGeom, new THREE.LineBasicMaterial({
color: "orange"
}));
scene.add(line);
var sPoints = new THREE.Points(lineGeom, new THREE.PointsMaterial({
size: .0312,
color: "yellow"
}));
scene.add(sPoints);
var unitPoints = [];
for (let i = 0; i < spline.getLength(); i++) {
let p = spline.getUtoTmapping(0, i);
let p1 = spline.getPoint(p);
unitPoints.push(p1);
}
var unitPointsGeometry = new THREE.Geometry();
unitPointsGeometry.vertices = unitPoints;
var units = new THREE.Points(unitPointsGeometry, new THREE.PointsMaterial({
size: .125,
color: "maroon"
}));
scene.add(units);
var marker = new THREE.Mesh(new THREE.SphereGeometry(0.125, 4, 2), new THREE.MeshBasicMaterial({
color: "red",
wireframe: true
}));
marker.geometry.translate(0, 0, 0.0625);
marker.geometry.vertices[2].z = 0.25;
marker.geometry.vertices[4].z = 0;
scene.add(marker);
var markerLineGeometry = new THREE.Geometry();
markerLineGeometry.vertices.push(new THREE.Vector3(), new THREE.Vector3());
var line = new THREE.Line(markerLineGeometry, new THREE.LineBasicMaterial({
color: "white"
}));
scene.add(line);
var clock = new THREE.Clock();
var progress = 0;
var totalLength = spline.getLength();
var speed = .66; // unit a second
var ratio = speed / totalLength;
var shift = 0;
var basePoint = 0;
var lookAtPoint = 0;
var oldPosition = spline.getPoint(0);
var speedVector = new THREE.Vector3();
function setProgress(delta) {
if (progress > totalLength) progress = 0;
shift = progress + speed * 2;
shift = shift > totalLength ? shift - totalLength : shift;
basePoint = spline.getUtoTmapping(0, progress);
lookAtPoint = spline.getUtoTmapping(0, shift);
line.geometry.vertices[0].copy(spline.getPoint(basePoint));
line.geometry.vertices[1].copy(spline.getPoint(lookAtPoint));
line.geometry.verticesNeedUpdate = true;
marker.position.copy(line.geometry.vertices[0]);
marker.lookAt(line.geometry.vertices[1]);
progress += speed * delta;
}
render();
function render() {
requestAnimationFrame(render);
setProgress(clock.getDelta());
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

three.js animation logic for spinning cuboids

I'm trying to create a cuboid from 5 cuboids one of top of the other - that rotates 180 CW degrees every two seconds.
It's kinda working but I'm not getting smooth results... I thought a good strategy would be that during the rotating period- the z-position of the top-left and top-right vertices of a single face will be compared- if they are the same then it's time to stop the rotating period.
The problem is that the difference between the vertices is never zero.. it's very close to zero... so I check if it's in a margin of 0.1 and thus I have a problem with the beginning of a rotation that is jammed because sometimes it's less than 0.1. Also sometimes the rotations continue when they should stop because the difference is not less than 0.1.
var spinningPeriod = false, counter = 0, lastTime = 0;
function animate(){
counter++;
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
var angleChange = 0.2 * timeDiff * 2 * Math.PI / 1000;
if (counter%200==0 && counter > 0) {
spinningPeriod = true;
}
if (spinningPeriod) {
var v1 = cubes[0].geometry.vertices[0].clone();
var v2 = cubes[0].geometry.vertices[3].clone();
cubes[0].updateMatrixWorld();
cubes[0].localToWorld(v1);
cubes[0].localToWorld(v2);
if (Math.abs(v1.x - v2.x) < 0.1) {
spinningPeriod = false;
}
for (var ii =0; ii<5; ++ii) {
cubes[ii].rotation.y += angleChange;
}
}
lastTime = time;
renderer.render(scene, camera);
// request new frame
requestAnimationFrame(function(){
animate();
});
}
// renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// camera
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 500;
// scene
var scene = new THREE.Scene();
// cube
var cubes = new Array();
var currentHeight = -150;
for (var ii =0; ii<5; ++ii) {
cubes[ii] = new THREE.Mesh(new THREE.CubeGeometry(400, 50, 20));
currentHeight += 50;
cubes[ii].position.y = currentHeight;
cubes[ii].overdraw = true;
scene.add(cubes[ii]);
}
// start animation
animate();
I agree, looking at the position is probably not the best way to do it. If you're concerned with the angle, then the angle should be your focus. Basically you want to have your test be based on the spin vs a max spin - when you're more than that, stop and adjust as needed (in this case, it was sometimes > 1 with the amount of angleChange, so I just reset it to 1).
Instead of your animate function above, try this one - it looks like it works pretty reliably for me.
var spinningPeriod = false, counter = 0, lastTime = 0;
var spinAngle = 0, spinMax = 1 * Math.PI;
function animate(){
counter++;
var time = (new Date()).getTime(),
timeDiff = time - lastTime,
speed = 0.2 * 2 * Math.PI / 1000,
angleChange = speed * timeDiff;
if (counter%200==0 && counter > 0) {
spinningPeriod = true;
spinAngle = 0;
}
console.log(counter, spinningPeriod, cubes[0].rotation.y / Math.PI);
if (spinningPeriod) {
spinAngle += angleChange;
if (spinAngle > spinMax) {
spinningPeriod = false;
spinAngle = spinMax;
}
for (var ii =0; ii<5; ++ii) {
cubes[ii].rotation.y = spinAngle;
}
}
lastTime = time;
renderer.render(scene, camera);
// request new frame
requestAnimationFrame(function(){
animate();
});
}

Accessing single particles in THREE.js Particle System

I really tried every example, searched the web for hours but I can't seem to get it working!
So I simply tried to implement a little particle system simulating falling snow, just like this: http://www.aerotwist.com/tutorials/creating-particles-with-three-js/
But I only can access it in whole. Meaning I can rotate it as such but as soon as I try to iterate over it's vertices, the whole animation is getting the hiccups! I would really appreciate some help here!
-
Here are the key parts:
-> Setting up the particle system:
var partikelGeo = new THREE.Geometry();
var partikelMaterial = new THREE.ParticleBasicMaterial({
color:0xffffff,
size: 10,
map: THREE.ImageUtils.loadTexture('snowflake2.png'),
blending: THREE.AdditiveBlending,
transparent:true
});
var partikelAnzahl = 3500;
for (var p = 0; p < partikelAnzahl; p++) {
var pX = Math.random() * 1000 -500;
var pY = Math.random() * 1000 -500;
var pZ = Math.random() * 1000 -500;
var partikel = new THREE.Vertex(new THREE.Vector3(pX,pY,pZ));
partikel.velocity = new THREE.Vector3(0,-Math.random(),0);
partikelGeo.vertices.push(partikel);
}
var partikelSystem = new THREE.ParticleSystem(partikelGeo, partikelMaterial);
partikelSystem.sortParticles = true;
scene.add(partikelSystem);
-> Rendering & Animation on mouseclick
var frame = 0;
function animate(){
// request new frame
requestAnimationFrame(function(){
animate();
});
// render
render();
}
animate();
var check = 0;
onmousedown = function(){
if (check) {
check = 0;
}else{
check = 1;
}
}
function render() {
if (check) {
clickDo();
}
camera.lookAt(new THREE.Vector3(0,0,0));
renderer.render(scene,camera);
}
function clickDo() {
frame++;
partikelSystem.rotation.y += 0.01;
var pCount = partikelAnzahl;
while(pCount--) {
// get the particle
var particle =
partikelGeo.vertices[pCount];
// check if we need to reset
if(particle.position.y < -200) {
particle.position.y = 200;
particle.velocity.y = 0;
}
// update the velocity with
// a splat of randomniz
particle.velocity.y -=
Math.random() * .1;
// and the position
particle.position.addSelf(
particle.velocity);
}
// flag to the particle system
// that we've changed its vertices.
partikelSystem.
geometry.
__dirtyVertices = true;
}
Rah
Your code looks good to me. I would just suggest to try not sorting your particles as you use an additive blending:
partikelSystem.sortParticles = false;

Resources