1
\$\begingroup\$

I'm following a tutorial on YouTube about procedural generation of planets. I'm puzzled in some aspects of the code shown. If any of you could explain the following points, I would be very pleased.

First take a look at the algorithm, written in C# for Unity:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TerrainFace {

    Mesh mesh;
    int resolution;
    Vector3 localUp;
    Vector3 axisA;
    Vector3 axisB;

    public TerrainFace(Mesh mesh, int resolution, Vector3 localUp)
    {
        this.mesh = mesh;
        this.resolution = resolution;
        this.localUp = localUp;

        axisA = new Vector3(localUp.y, localUp.z, localUp.x);
        axisB = Vector3.Cross(localUp, axisA);
    }

    public void ConstructMesh()
    {
        Vector3[] vertices = new Vector3[resolution * resolution];
        int[] triangles = new int[(resolution - 1) * (resolution - 1) * 6];
        int triIndex = 0;

        for (int y = 0; y < resolution; y++)
        {
            for (int x = 0; x < resolution; x++)
            {
                int i = x + y * resolution;
                Vector2 percent = new Vector2(x, y) / (resolution - 1);
                Vector3 pointOnUnitCube = localUp + (percent.x - .5f) * 2 * axisA + (percent.y - .5f) * 2 * axisB;
                Vector3 pointOnUnitSphere = pointOnUnitCube.normalized;
                vertices[i] = pointOnUnitSphere;

                if (x != resolution - 1 && y != resolution - 1)
                {
                    triangles[triIndex] = i;
                    triangles[triIndex + 1] = i + resolution + 1;
                    triangles[triIndex + 2] = i + resolution;

                    triangles[triIndex + 3] = i;
                    triangles[triIndex + 4] = i + 1;
                    triangles[triIndex + 5] = i + resolution + 1;
                    triIndex += 6;
                }
            }
        }
        mesh.Clear();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
    }
}

I will briefly explain the code:

The main objective is to generate 6 Meshes which will form a cube, and then by normalization of vectors generate a sphere. This algorithm takes a "Resolution" value, which will determine the how detailed the sphere will be. Each mesh is conformed by vertices (Vector) which will guide the program to make the triangles that will build the sphere.

My question regard in this part of the code:


Mesh mesh;
    int resolution;
    Vector3 localUp;
    Vector3 axisA;
    Vector3 axisB;

    public TerrainFace(Mesh mesh, int resolution, Vector3 localUp)
    {
        this.mesh = mesh;
        this.resolution = resolution;
        this.localUp = localUp;

        axisA = new Vector3(localUp.y, localUp.z, localUp.x);
        axisB = Vector3.Cross(localUp, axisA);
    }

I do not understand exactly what is localUp and the pair of axes it generates from it.

So the questions would be the following:

  • What are localUp, axisA and axisB?
  • What are they doing?
  • If they have values (coordinates or something like that) where are they getting those.?
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

This code is establishing a local coordinate system in which to perform the mesh generation.

When we want to generate the 6 cube faces, we need to iterate over the rows and columns of a grid to place each vertex. Each time we loop, we need to step "across" a row, or "along" a column of the grid. But since the 6 faces are oriented in different directions, it initially looks like we might have to write 6 versions of the code!

  • One for the "right" face (facing x+), where:
    • moving "across" means stepping in the z+ direction,
    • moving "along" means stepping in the y- direction
  • One for the "top" face (facing y+), where:
    • moving "across" means stepping in the x+ direction,
    • moving "along" means stepping in the z- direction
  • One for the "front" face (facing z+), where:
    • moving "across" means stepping in the y+ direction,
    • moving "along" means stepping in the x- direction
  • One for the "left" face (facing x-), where:
    • moving "across" means stepping in the z- direction,
    • moving "along" means stepping in the y- direction
  • One for the "bottom" face (facing y-), where:
    • moving "across" means stepping in the x- direction,
    • moving "along" means stepping in the z- direction
  • One for the "back" face (facing z-), where:
    • moving "across" means stepping in the y- direction,
    • moving "along" means stepping in the x- direction

(If these definitions for "across" and "along" look arbitrary: they are indeed. There are many possible ways to parametrize 6 grids around a cube - what I've shown above is the convention that the code from the video happens to use)

We don't want to write this 6 times, so instead we make variables to store the different directions that we need to combine to make our vertex positions:

  • The direction this side of the cube "faces" we call localUp
  • The direction "across" a row we call axisA
  • The direction "along" a column we call axisB

localUp would be passed to us as a constructor argument. So somewhere else in the code there's probably something that looks like this (or something to the same effect):

//                                      localUp          value of localUp
face[0] = new TerrainFace(mesh[0], res, Vector3.right);   // ( 1,  0,  0)
face[1] = new TerrainFace(mesh[1], res, Vector3.up);      // ( 0,  1,  0)
face[2] = new TerrainFace(mesh[2], res, Vector3.forward); // ( 0,  0,  1)
face[3] = new TerrainFace(mesh[3], res, Vector3.left);    // (-1,  0,  0)
face[4] = new TerrainFace(mesh[4], res, Vector3.down);    // ( 0, -1,  0)
face[5] = new TerrainFace(mesh[5], res, Vector3.back);    // ( 0,  0, -1)

Each time, we're passing a unit vector pointing directly along one of the 6 cardinal directions in 3D.

We can make the "across" direction axisA by "swizzling" this vector - swapping the value in each component with the next one, wrapping around. So (0, 1, 0) becomes (1, 0, 0) and (1, 0, 0) becomes (0, 0, 1).

You can see that if we started out with a cardinal direction, we get back a different cardinal direction (unit length, and perpendicular to what we started with). So that gives us a vector we can use as one direction across our grid.

To get the "along" direction axisB, we take the cross product of the two vectors we have so far. This has a nice property: the cross product of two vectors is always perpendicular to both (so in this case, it points along the remaining direction of our grid). And if those vectors were already perpendicular to each other and unit length, our output will be too.

Now, there are two possible vectors perpendicular to our two starting vectors (one -1 times the other), and which one we get depends on the order of the arguments to Cross(). By using one standard order (localUp cross axisA), we ensure the grid is oriented so that the triangles we're building always point outward, rather than inward. That's why we're using the cross product here instead of another swizzle, which might sometimes pick the wrong perpendicular out of our two choices.

Now that we have these three vectors - what we'd call a "local coordinate basis", we can express the rest of our mesh generation in terms of these vectors: the vertex at coordinates (u, v) in our grid should be placed at localUp + u * axisA + v * axisB, and this works on all 6 faces, so we don't have to write 6 versions of the function.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much!!!! Very helpful \$\endgroup\$ Feb 25, 2022 at 19:58
-1
\$\begingroup\$

I'm not so sure about my answer but here's what I know:

localUp (shown in green) is a Vector - that is perpendicular to the screen - which changes when the object rotates.

axisA is just a copy of localUp but the x and y values are swapped to get the red Vector and axisB is just the cross product of localUp and axisA to find the blue Vector

enter image description here

\$\endgroup\$
4
  • \$\begingroup\$ The question asks about the 6 faces of a planet cube. How does that relate to "the screen" here? I assume you're not talking about the player's monitor? \$\endgroup\$
    – DMGregory
    Feb 24, 2022 at 23:25
  • \$\begingroup\$ By "screen" I meant one of the sides of a cube \$\endgroup\$
    – Arian_ki
    Feb 25, 2022 at 12:18
  • \$\begingroup\$ Thank you very much!! \$\endgroup\$ Feb 25, 2022 at 19:58
  • \$\begingroup\$ You're welcome! Accept the answer if it helped you so others with the same question would find it easier \$\endgroup\$
    – Arian_ki
    Feb 25, 2022 at 20:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .