Creating custom mesh Morph Target Animations using THREE.js

I'm currently working on creating an animation that showcases the revolution of a 2D function around an axis to produce a 3D surface. Successfully rendering the surface, my next task is to implement the animation section using MorphTargets.

Despite managing to push all the morph frames into my geometry, I'm encountering difficulties in getting the animation to run smoothly. The animation examples I've come across typically involve importing models from Blender, which isn't entirely beneficial (and many are outdated).

In trying to replicate the code closely to the morphing horse example (), I still haven't achieved success.

Below is my code for initializing the morphVerticeHolder, a 2D array meant to store the vertices for each animation frame:

// First indices of multiarray corresponds to morph frame, second to the vertice
var morphVerticeHolder = [];
for (var i = 0; i < 20; i++) {
   morphVerticeHolder[i] = [];
}

Here's the code snippet where I push the vertices along the axis while simultaneously adding the morph vertices. 'mI' refers to the mesh interval:

for(var i = xstart; i <= xend; i += mI) {
  geo.vertices.push(new THREE.Vector3(i, 0, 0));
  for (var j = 0; j < 20; j++) { // 20 Frames of the morph animation
    morphVerticeHolder[j].push(new THREE.Vector3(i, 0, 0));
  }
}

This section pushes all the vertices, with 'i' iterating along the x-axis and 'j' spanning theta to populate vertices for the mesh:

// Push the vertices
var tmesh = 2 * Math.PI / meshPoints; // Theta mesh interval
verticeCounter = 0;
for (var i = xstart; i <= xend; i += mI) {
    var rMain = solveEquation(i);
    var rSecond = solveEquation(i + mI);
    var counter = 0;
    for (var j = 0; j < 2 * Math.PI; j += tmesh) {
        geo.vertices.push(new THREE.Vector3(i, rMain * Math.cos(j / 1000), rMain * Math.sin(j / 1000)));
        for (var k = 0; k < 20; k++) {
            morphVerticeHolder[k].push(new THREE.Vector3(i, rMain * Math.cos(j * (k + 1) / 20), rMain * Math.sin(j * (k + 1) / 20)));
        }
    }
}

The division by 1000 aims to initiate the original mesh close to zero and then animate its rotation using morph vertices ranging from 1/20 to 20/20 of the desired value.

Next, I proceed to push the faces, and even though the algorithm may seem complex, it serves to keep track of the position of each vertex:

for(var i = 0; i < meshPoints; i++) {
    var sI = meshPoints * (i + 1);
    var sI2 = meshPoints * (i + 2);
    for (var j = 0; j < meshPoints - 1; j ++) {
        if (i === 0) {
            // Fill end at first point
            geo.faces.push(new THREE.Face3(sI + j, sI + j + 1, 0));
            geo.faces.push(new THREE.Face3(sI + j + 1, sI + j, 0));
            // Fill body
            geo.faces.push(new THREE.Face3(sI + j, sI + j + 1, sI2 + j));
            geo.faces.push(new THREE.Face3(sI + j + 1, sI + j, sI2 + j));
            geo.faces.push(new THREE.Face3(sI + j + 1, sI2 + j, sI2 + j + 1));
            geo.faces.push(new THREE.Face3(sI2 + j, sI + j + 1, sI2 + j + 1));
        } else if (i === meshPoints - 1) {
            // Fill end at last point
            geo.faces.push(new THREE.Face3(sI + j, sI + j + 1, meshPoints - 1));
            geo.faces.push(new THREE.Face3(sI + j + 1, sI + j, meshPoints - 1));
        } else {
            geo.faces.push(new THREE.Face3(sI + j, sI + j + 1, sI2 + j));
            geo.faces.push(new THREE.Face3(sI + j + 1, sI + j, sI2 + j));
            geo.faces.push(new THREE.Face3(sI + j + 1, sI2 + j, sI2 + j + 1));
            geo.faces.push(new THREE.Face3(sI2 + j, sI + j + 1, sI2 + j + 1));
        }
    }
}

Finishing up with initialization steps and other necessary configurations:

for (var k = 0; k < 20; k++) {
    geo.morphTargets.push({ name: "target" + k, vertices: morphVerticeHolder[k] });
}

// Additional configuration for rendering
var uniforms = {
    resolution: { type: "v2", value: new THREE.Vector2 },
    zmax: { type: "f", value: maxz},
    zmin: { type: "f", value: minz}
};
var mat = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: document.getElementById('cubeVertexShader').innerHTML,
    fragmentShader: document.getElementById('cubeFragmentShader').innerHTML
});
functionObject = new THREE.Mesh(geo, mat);
this.scene.add(functionObject);
functionObject.name = 'current';

this.animation = new THREE.MorphAnimation(functionObject, 'Revolution');
this.animation.play();

this.setWithinRender(function() {
    this.animation.update(.1);
});

Note that 'this.setWithinRender' integrates the anonymous function into the render loop of THREE.js (unlike typical setup methods).

While the mesh renders successfully, eliminating the '/1000' when pushing the original vertices results in displaying the entire surface. However, the animation, structured as above, fails to execute. Any insights or assistance on this matter would be greatly appreciated!

Thank you!

Answer №1

After doing some additional research and exploring the source code further, I made a noteworthy discovery regarding the use of certain materials in my project. It became evident that for the material to function properly, the morphTargets attribute needed to be set to true. Despite initially attempting this with the shader material I was utilizing, it required manual application of the morphTargetInfluences in the vertex shader.

As a solution, I decided to switch the material to

var mat = new THREE.MeshNormalMaterial( { color: 0x990000, morphTargets: true } );

and fortunately, everything fell into place seamlessly!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Refine Your Grid with a Pinterest-Inspired

I've set up a grid of images in columns similar to Pinterest that I would like to filter. The images vary in height but all have the same width. The issue arises when a taller image is followed by a shorter one, causing the short image to float right ...

Extract several "documents" from one compilation

To easily convert my code into a single module using webpack, I can use the following method: { entry: path.join(__dirname, 'src/index.js'), output: { path: path.join(__dirname, 'dist'), filename: 'bundle.js', ...

Is there a potential impact on performance when utilizing local variables instead of repeatedly accessing properties?

Examining JavaScript code optimized for a performance-sensitive environment, specifically a game engine in mobile settings. Oftentimes, this code avoids using local variables and instead relies on explicit chains, such as: if (this.x.y[z].i) { this.x ...

In the realm of jQuery, erasing dynamically generated elements across multiple elements sharing the same index is a task

Currently, I am enhancing the select box dropdown and list of new fonts by adding custom font names. In addition to this, I also want to incorporate a feature for deleting fonts. For instance, when I delete font A from the list (which is currently functio ...

Using CodeIgniter framework for processing form submissions asynchronously using AJAX

I'm currently facing a challenge where I need to save data submitted from a form to my mysql database, and then update the div element with the latest posted item at the beginning of the list in the div. At this point, my main focus is on receiving a ...

What is the best way to delete an added element once a DIV has been toggled with JQuery?

I'm facing an issue where I need to add an element to a DIV that has a toggle function. However, every time I click the toggle again after adding the element, the same element is appended once more, resulting in duplicate elements. Upon observation, ...

A TypeScript class transferring data to a different class

I have a set of class values that I need to store in another class. function retainValues(data1,data2){ this.first = data1; this.second = data2; } I am looking for a way to save these class values in a different class like this -> let other = N ...

Upgrade button-group to dropdown on small screens using Bootstrap 4

I am currently developing a web application and incorporating Bootstrap 4 for certain components such as forms and tables. Within the design, I have included buttons grouped together to display various actions. Below is an example code snippet: <li ...

Are there any factors within a local network or desktop environment that may impact the execution of JScript?

Something strange is happening with the JavaScript on my project. It works perfectly fine, except when accessed from computers at a specific company. Even more puzzling is that the JavaScript only fails about half of the time when accessed from that compan ...

Error message while running jQuery 3.2.1: issues with UL and LI

I have a link that displays the desired outcome Despite using jquery version 3.2.1, when I insert my code into Dreamweaver cc 2018, it fails to work. On my jsfiddle, the selection is sorted alphabetically, but on my web page it is not. var mylist = $( ...

Struggling with integrating API response formatting in React and updating state with the data

This is the content found in the file 'Search.js' import React, { Component } from 'react'; import * as BooksAPI from './BooksAPI' class Search extends Component{ constructor(props){ super(props); this.state={ ...

Ways to track all requests completed in Nuxt 3

I am looking to create a unique loading page that conceals itself once all requests and static data have been loaded, including static images and libraries. How can I determine when all requests and static data have finished loading? I have attempted the ...

best way to retrieve state from redux-toolkit (excluding initial state) in a Next.js environment

I am attempting to access the state of redux-toolkit in Next.js's getStaticProps (After saving the accessToken in the store, I need to access the store from getstaticprops for the API it requires) Here's what I have tried: export default functi ...

Tips for isolating all the Javascript loaded via ajax or jquery.load within a specific child scope instead of a global scope

I am currently working on a solution to embed a third-party page into my site using ajax/jquery.load() to avoid using iframes (CORS requirements are already handled by the third party). My dilemma lies in the fact that the main host site loads jquery 1.x ...

My server keeps crashing due to an Express.js API call

I'm completely new to express.js and API calls, and I'm stuck trying to figure out why my server keeps crashing. It works fine the first time, rendering the page successfully, but then crashes with the error: TypeError: Cannot read property &apo ...

Refreshing the information in the database table

Upon receiving data from the server using ajax, I populate this table: $.each(data, function(i, item) { $('#MyTable tbody').append("<tr>" +"<td>" +data[i].A+ "</td><td>" +data[i].B ...

What is the process of directing a data stream into a function that is stored as a constant?

In the scenario I'm facing, the example provided by Google is functional but it relies on using pipe. My particular situation involves listening to a websocket that transmits packets every 20ms. However, after conducting some research, I have not foun ...

Having trouble getting Three.js JSON models to cast shadows properly?

Recently, I've been experimenting with blender exported models in Three.js. I have successfully imported a model and observed how light interacts with the object. While a directionalLight is illuminating the front-facing parts, I'm facing an issu ...

VueJS - repeating input fields for file uploads

I need help removing duplicate items from an array in JavaScript, but when I try to delete one, it always deletes the last occurrence! https://i.sstatic.net/NeJRJ.jpg let app = new Vue({ el: '#app', data: { items: [] }, methods: { ...

ReactJs Error: Unable to access property value because it is undefined (trying to read '0')

I am currently attempting to retrieve and display the key-value pairs in payload from my JSON data. If the key exists in the array countTargetOptions, I want to show it in a component. However, I am encountering an error message stating Uncaught TypeError ...