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!