3D spinning icosahedron adorned with circles at each corner using three.js

In my project, I am working with an interactive icosahedron mesh that undergoes rotation. As part of the animation loop, circle geometries are dynamically added, and their locations are set to each vertex of the mesh at every frame.

geometry = new THREE.IcosahedronGeometry(isoRadius, 1);
var material = new THREE.MeshBasicMaterial({
    color: wireframeColor,
    wireframe: true
});

isoMesh = new THREE.Mesh(geometry, material);
scene.add(isoMesh);

The circle geometries' positions are updated as the icosahedron rotates:

function animate() {

    isoMesh.rotation.x += 0.005;
    isoMesh.rotation.y += 0.002;

    // update vertices
    isoMesh.updateMatrix(); 
    isoMesh.geometry.applyMatrix(isoMesh.matrix);
    isoMesh.rotation.set(0, 0, 0);

    for (var i = 0; i < geometry.vertices.length; i++) {

        nodes[i].position.copy(geometry.vertices[i]);
        nodes[i].lookAt(camera.position);
    }

I've noticed a peculiar behavior where if I remove the line "isoMesh.rotation.set(0, 0, 0);", the icosahedron rotates correctly but the nodes rotate too quickly. However, when I add that line back in, the nodes rotate correctly but the icosahedron doesn't move. This discrepancy between the rotations of the nodes and the mesh has me puzzled.

My understanding of three.js is still limited, so I'm unsure why this difference in rotation behavior occurs. It seems there may be a distinction between the mesh and the geometry, leading to this issue. Can anyone shed light on what might be causing this unexpected outcome?

Answer №1

The solution is multi-faceted.

Your Dodecahedron:

You made good progress by rotating your dodecahedron and its vertices. Instead of applying the rotation to all vertices (which would result in drastic rotation), apply it only to the mesh. However, this won't update the vertices automatically. Let's address that shortly.

Your Circles:

Placing them at each vertex is a step in the right direction, but as mentioned by WestLangley, using lookAt for objects with rotated/transferred parents won't work. Therefore, you should add the circles directly to the scene. If you can't determine the new positions of the vertices after rotation, the circles will stay fixed in place. We need to obtain those updated vertex positions.

Obtaining Updated Vertex Positions:

While rotating the mesh updates its transformation matrix rather than the vertices, we can leverage this updated transformation matrix to determine the updated positions for the circles. The method Object3D.localToWorld enables us to convert a local THREE.Vector3 (such as your dodecahedron's vertices) into world coordinates. (Note that cloning each vertex is necessary because localToWorld modifies the provided THREE.Vector3).

Key Points:

To focus on elements related to your query, I've compiled relevant JavaScript code in the snippet below.

  • Avoid modifying geometry unnecessarily.
  • Reserve lookAt for objects in the global coordinate system.
  • Utilize localToWorld and worldToLocal for vector transformations across different coordinate systems.

// This part was already implemented
var geometry = new THREE.DodecahedronGeometry(10, 1);
var material = new THREE.MeshBasicMaterial({
    color: "blue",
    wireframe: true
});

var dodeMesh = new THREE.Mesh(geometry, material);
scene.add(dodeMesh);

// Directly add your circles to the scene
var nodes = [];
for(var i = 0, l = geometry.vertices.length; i < l; ++i){
  nodes.push(new THREE.Mesh(new THREE.CircleGeometry(1, 32), material));
  scene.add(nodes[nodes.length - 1]);
}

// Call this function in render. Retrieve world positions of vertices and assign them to the circles.
var tempVector = new THREE.Vector3();
function updateVertices(){
    if(typeof dodeMesh !== "undefined" && typeof nodes !== "undefined" && nodes.length === dodeMesh.geometry.vertices.length){
    dodeMesh.rotation.x += 0.005;
    dodeMesh.rotation.y += 0.002;
    for(var i = 0, l = nodes.length; i < l; ++i){
      tempVector.copy(dodeMesh.geometry.vertices[i]);
      nodes[i].position.copy(dodeMesh.localToWorld(tempVector));
      nodes[i].lookAt(camera.position);
    }
  }
}
html *{
padding: 0;
margin: 0;
width: 100%;
overflow: hidden;
}

#host {
width: 100%;
height: 100%;
}
<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<div id="host"></div>

<script>
// INITIALIZATION
var WIDTH = window.innerWidth,
    HEIGHT = window.innerHeight,
    FOV = 35,
    NEAR = 1,
    FAR = 1000;

var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
document.getElementById('host').appendChild(renderer.domElement);

var stats= new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);


var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;

var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 5.0; // Increase rotation speed slightly

var scene = new THREE.Scene();

var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);

scene.add(light);

function render(){
  if(typeof updateVertices !== "undefined"){
    updateVertices();
  }
  renderer.render(scene, camera);
  stats.update();
}

function animate(){
  requestAnimationFrame(animate);
  trackballControl.update();
  render();
}

animate();
</script>

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

Create an array populated with unclosed HTML tags that need to be rendered

I'm trying to display a simple list, but it seems like React is having trouble rendering unclosed HTML elements that are pushed onto the list. This results in an error: ReferenceError: el is not defined If I use single quotes (') bookingStat ...

Tips for resolving the setAttribute() function error message: "Argument of type 'boolean' is not assignable to parameter of type 'string'"

I am currently working on a function that dynamically updates the HTML aria-expanded attribute based on whether it is true or false. However, when I declare element as HTMLElement, I encounter an error stating Argument of type 'boolean' is not as ...

Prevent clicking on form until setInterval has been cleared using React useEffect

Here is a link to a sandbox replicating the behavior: sandbox demo I have integrated a hook in a React component to act as a countdown for answering a question. React.useEffect(() => { const timer = setInterval(() => { setTimeLeft((n ...

Using Javascript within HTML: A guide on when to utilize semi-colons and when to implement the return false statement

Exploring the use of JavaScript attributes within HTML elements: <input type="button" onclick="somceFunc()" /> <input type="button" onclick="somceFunc();" /> <input type="button" onclick="somceFunc(); return false;" /> Debating whether ...

Javascript handling scrolling and repositioning

When using the scrollBy() method in the window object of JavaScript, there are two arguments required. But what do these arguments actually represent? The resource I am currently studying from states that it is "the number of pixels to scroll by," but I am ...

Is there a way to eliminate the mongoose event listener registered onConnect in the Socket.io Leak issue?

Whenever a user connects via socket.io, the following subscribe event is triggered: socket.on('subscribe', function(room) { console.log('joining room', room.id); socket.join(room.id); socket.roomName = room.id; // ...

Leveraging the power of Express.js, transmit local data alongside a

Why can't I display the active user in my view using plain HTML when console log shows it? Here is the code snippet: app.post('/', function (req, res) { var user = { user : req.body.username }; res.render('doctor_hagfish/pets&ap ...

Retrieve the text label from the parent option group

Below is the HTML code snippet: <select id="categoryg"> <optgroup label="Main" value="binding"> <optgroup label="Sub" value="binding"> <option value="46&quo ...

What is the method for determining the gaps between cells in a grid-based puzzle game similar to Sudoku?

this is my current code and it's successfully functioning var cellSize:Number = 36; var cellGap:Number = 4; var row:Number; var col:Number; for (var a:int = 0 ; a < puzzleSTR.length ; a++) { col = a % 9; row = Math.floor(a / 9); ...

Removing/modifying selected choices in an element

I have implemented a material ui select element with the ability to make multiple selections using checkboxes. My query is, can I incorporate the functionality to delete or update names directly from the select element itself? For instance, by clicking on ...

Why does parsing the GET response fail intermittently with Jquery, ajax, and JSON?

Currently, I am encountering an issue with ajax calls using jQuery where the response being returned is a JSON array. In certain scenarios, everything works perfectly fine. However, in other cases specifically in browsers like Firefox and IE11, there seems ...

Having trouble retrieving directive parameters in Vue.js?

Vue.directive('customselect', { params: ['selectedTask'], bind: function () { var that = this; $(this.el) .select2() .on('change', function () { that.set(this.value); if (!this.name.matc ...

Can Vuex mapActions be utilized within a module that is exported?

Is it possible to utilize Vuex mapActions from an external module imported into a component? I am working on standardizing a set of functions in a vue.js web application. My goal is to import these functions into each component and pass necessary values f ...

The pagination functionality in the customized React Native list component is malfunctioning

In my customized list component known as TableList, there is a pagination functionality implemented. However, a peculiar behavior occurs when the user interacts with the pagination arrows. Upon clicking either the next or previous arrow for the first time ...

Unable to assign a value to a variable within a Mongoose callback function

I am struggling to comprehend why the variable "productsAvailable" still shows true even after being set to false. router.post('/api/transactions', (req, res) => { var productsAvailable = true for(var i=0; i<3; i++) { Prod ...

What methods can I use to insert an array of objects into a Query?

I'm currently trying to understand how I can pass an array of objects into my GraphQL query. The documentation seems a bit confusing on this matter. In my project, I am using Apollo on the frontend, Graphql-yoga on the backend, and Prisma as my databa ...

Converting Rails data into JSON objects using hashing techniques

I'm currently working on a Rails query that looks like this: @c = RealEstateAgentAssignmentStatus.joins(:real_estate_agent_assignments =>:loan_application) .group("real_estate_agent_assignment_statuses.assignment_status").select("real_estate_a ...

The DAT GUI controls are mysteriously absent from the scene

Within a modal, I have set up a threejs scene with three point lights. All functions are exported from a separate file called three.ts to the modal component. The issue I am facing is that when I try to initialize DAT.GUI controls, they end up rendering ...

Ways to conceal the player controls with mediaelementjs.com

Is there a way to display the control bar only when the user hovers over the video while using the mediaelementjs.com player? I feel like there might already be a function for this, but I can't seem to find it anywhere. Should I just implement a simpl ...

Don't leap to the top of the page, take your time!

This is the question I have posed <a href="#" onclick="return false">show_all</a> I attempted onclick=preventDefault(); onclick="return false"; However, it is continuing to jump to the top of the page. Any recommendations on how to pr ...