WP2WS Project 1 Algorithmic 3D Visualization/Animation

Decompose

Link to the Code

Link to the Live Site

Demo

 

Inspiration & Ideation

The project is inspired by my Week3 assignment and The Three-body Problem.

Threebody.jpg

In The Three-body Problem series1, the people at the Battle Command Center launched the project “Operation Guzheng” “string the nanomaterial right across the narrowest point of the canal, at the Gaillard Cut” (Link). They wanted to cut a warship into 12 pieces with nanomaterial strings, to kill Evan and his supporters, as well as destroy the machine that stored the messages from Three-body aliens. In series3, the solar system is struck by the dimensionality reduction blow, which reduces the three-dimensional substances into 2D (Link). I was so shocked by the two scenes when I first read the book. And my project simulates the motion of “cut” and “dimensional reduction”. The aesthetic is inspired by the code rain in The Matrix.

Although my initial goal is to achieve morphing between a baby head and an aged head to express the “aging”, I’m happy with the change of direction and the final result.

Process

I get started with the sphere geometry, which is also a buffergeo and has vertices positions. 

I found that the number of vertices reaches 255967, which is under the 1 million threshold mentioned by Prof Moon in class.

When the model is loaded, the browser runs very slowly

And I failed to scale the model up by scaling the scene. I later learned that when the geometry is scaled, the vertices array wouldn’t be updated because the information is local inside the gltf file structure and would remain in its original position (not very sure about this explanation though). After consulting prof Moon, I found that I can update the points by multiplying the vertices xyz position with the same number:

for (let i = 0; i < vertices.length; i++) {
   vertices[i] *= 10;
}

To make the computation less expensive, I reduced the number of points through skipping vertices:

for (let i = 0; i < vertices.length; i += 24) {
   let p = new Particle();
   p.setPosition(
      vertices[i * 3],
      vertices[i * 3 + 1],
      vertices[i * 3 + 2]
   ).setVelocity(0, 0, 0);
   particles.push(p);
}

 

Asynchronous function:

In the week 3 assignment, I have trouble accessing the vectors of the model. I tried to put a detector of finish loading at the draw() in p5js file. But that apparently doesn’t work. I was stuck in the thought that, since the initThree() is put in the p5js setup(), the Threejs file is set up only once and I have no way to loop through a detector and wait till the model is loaded. After meeting with Prof Moon, I found that the updateThree(), which is in the animate() in Threejs file, will be looped every frame. (I felt silly about this… how would a scene been animated without a looping function…) . I constructed the particles in the GLTF loading function and wait till the data is loaded to animate.

function processData(vertices) {
   // construct and push particles
}

Vola! Now that I can access the model vectors & particles.

Animating the pointCloud:

I tried to oscillate the points by multiplying the position vector with a sin() and passing in the frameCount:

update(frame) {
   this.pos.x *= sin(frame * 0.05) * 5;
   this.pos.y *= sin(frame * 0.05) * 5;
   this.pos.z *= sin(frame * 0.05) * 5;
}
animate(){
   frame++;
   updateThree(frame){
       for (let i = 0; i < particles.length; i++) {
           particles[i].update(frame);
       }
   }
}

But I get this, a single point with no error message appearing in the console window:

I then defined this.frame in the constructor, multiply a fixed value by the position, and update the this.frame every frame:

update() {
   this.pos.x *= 1.001;
   this.pos.y *= 1.001;
   this.pos.z *= 1.001;
   this.frame += 1;
}

The point values are updating fine. I realized that if I multiply the position value with sin(), the value would change crazily between negative and positive instead of scaling gradually, so I tried a smaller amplitude and do the addition:

update() {
   this.pos.x += sin(this.frame * 0.01) * 1.05;
   this.pos.y += sin(this.frame * 0.01) * 1.05;
   this.pos.z += sin(this.frame * 0.01) * 1.05;
   this.frame += 1;

I then map the sin(this.frame * 0.01) to the range between 0.5 – 1.5:

update() {
   let m = map(sin(this.frame * 0.01), -1, 1, 0.5, 1.5);
   this.pos.x *= m;
   this.pos.y *= m;
   this.pos.z *= m;
   this.frame += 1;

The result seems promising, but it scales too much so that it zooms out of the scene for a long time:

 

I further adjusted the parameters and finally set the range between 0.997 and 1.003:

 

I randomized the frequency and get this visual:

 

It looks like a body being decomposed. The visual is a little bit horrible… So I move on to my second idea.

Falling sand animation

I give a fix acceleration value to each particle and make them fall when mouse is clicked:

fall(ifclick) {
   if(ifclick){
      this.isClicked = true;
   }
   if(!this.isDone){
      if (this.isClicked){
         this.acc.x = random(3.5);
         this.vel.add(this.acc);
         this.pos.add(this.vel);
      }
   } else{
      this.acc.x = 0;
      this.vel.x = 0;
      this.pos.add(this.vel);
   }
}

The particle will stop when it’s on the “ground”:

checkOntoGround() {
   if (this.pos.x == 0) {
      this.isDone = true;
   }
}

 

 

After adjusting the parameters:

Morphing between Geos:

My intention was to make a baby head morph into an aged one (the two models are made in my previous digital sculpting class).

I realized that I have to decide which model has more vertices because if I create particles based on the one with fewer vertices, there won’t be enough particles when morphing. I then tested my idea by morphing between baby head and a sphere geo. 

I map the index of the remaining particles to the range of (0, geo1Vex) so that they would still go to the sphere vertices. I use leap for a gradual transformation from the model to the sphere. 

When the particle’s position equals to sphere’s corresponding vertex, I makes this.finMorph true to indicate that the particle has finished leaping and switch the model by multiplying geoIndex by -1.

However, I get an error message saying that the 

I learned stackflow that the problem is caused by calling a function inside a function and goes on and on forever.

I added the red part because I found that this.isClicked is always true even though the this.finMorph turned true, and it might be because the lerp runs too many times.  

if (ifclick && !this.finMorph) {
   this.isClicked = true;
} else {

   this.isClicked = false;


}
 

Not working still. I asked Joyce for help and she told me that this is probably due to too many particles (more than 30,000). I then loaded the camel model. But it doesn’t work either. 

Due to the time limit, I finally decided to abandon the morphing idea and further develop the sand-falling effect.

Point texture

I downloaded a texture from CityPNG and blend it with a yellow color to get the green glowing color effect. 

The dimensional strick

Without any controls, the particles would fall onto the decided ground.

 

To make the effect of the dimensional strike, I use the “ground” GUI to control where the particles would stop; the “initY” would lift up the particles; the “scale” would scale particles. 

 

I had some problems with scaling at first: although I can scale up the model, all the particles including the layered ones are scaled up, so that the final result is still layers with the same scale.

 

I added an if statement to solve this issue:

gui.add(params, "size", 0.9, 1.1).onChange(function (e) {
   for (let p of particles) {
      if (p.vel.y != 0) {
         p.pos.mult(e);
      }
   }
});

I also tried several other models:

 

Reflection

In this project, thanks to prof Moon’s help, I finally understand the concept of asynchronous functions and variable scope (I made a lot of similar mistakes due without knowing the root cause in my earlier projects…). I also practiced on the real-time GUI control and generative arts creation. 

Future improvements include: 

  1. the color change: I still have trouble changing the color of the particles… Will add color control to make multi-color layers.
  2. the interactions: now the control is not very user-friendly. I need to further adjust the threshold. And I want to also try other interactions such as hand gestures/mouse move. 
  3. The GUI: I want to add more controls in the future. e.g. the x, z position. 
  4. the morphing: I haven’t solved the morphing problem. Needs further debugging on that.
  5. the motion: right now I only apply a gravitational force to the particle (the acceleration is a fixed number). I may try to use attraction and repelling in the future to make the motion more interesting.
  6. Model shifting control: Currently, I can only change model through manually editing the code. It’s difficult to make a controller because every model has a different gltf file structure. 

Reference

  1. vertices not updated explanation: https://stackoverflow.com/questions/21506099/vertices-not-being-updated-after-changing-position-three-js
  2. models: the camel, penguin and mouse are downloaded from spine
  3. texture: https://www.citypng.com/photo/14213/hd-glowing-blue-turquoise-ball-effect-transparent-png

Leave a Reply

Your email address will not be published. Required fields are marked *