While they may appear solid, the 3D shapes that ShapeScript produces are actually hollow shells composed of polygons. These polygon shells are known as meshes.
ShapeScript provides many tools for constructing meshes, including primitives, builders and CSG operations, but you can also create meshes manually by specifying each polygon yourself. While this is generally a tedious approach compared to the other tools, it affords you complete control over the mesh topology, and allows you to create arbitrary shapes that might be hard to achieve with higher-level tools.
A mesh is made up of one or more polygons, and each polygon is made up of three or more vertices or points. To construct a mesh you use the mesh
, polygon
and point
commands. The following code defines the simplest possible mesh - a single triangle:
mesh {
polygon {
point 0 0
point 1 0
point 1 1
}
}
The point
command accepts a vector value. Polygons can be placed anywhere in three-dimensional space, so point
accepts up to three values (for the X, Y and Z coordinates respectively). In this case we’ve just specified two values, so the Z coordinate defaults to zero.
Manually constructed meshes do not currently support textures, but they inherit the current material color. Color can also be applied separately to individual vertices within a mesh. When vertices of a given polygon have different colors applied, the color will be smoothly interpolated between them, creating a gradient:
mesh {
polygon {
color red
point 0 0
color green
point 1 0
color blue
point 1 1
}
}
If you rotate the triangle you will see that the back face is invisible. Most of the mesh-construction commands in ShapeScript produce shapes that are watertight, meaning that they do not contain any holes that would allow you to see the back faces of the polygon surface, but when you create a mesh using the mesh
command it’s up to you to ensure that the mesh you create is watertight. You can check this by selecting the mesh in the editor and getting info. As you can see, our triangle is not watertight:
Watertightness is an important quality when using CSG operations, because the “solid” in Constructive Solid Geometry implies that shapes are expected to behave like solid objects (even though they are actually hollow), and holes or exposed back faces will cause glitches as ShapeScript is unable to determine whether a given point lies inside or outside the shape.
To solve this, we can add another triangle with the same vertices but facing the opposite direction. But wait - we never specified the direction of the triangle face in the first place, so how does ShapeScript decide which way a polygon is facing?
By convention, polygons in ShapeScript are assumed to be defined with counterclockwise (aka anticlockwise) winding. What that means is that when looking at the polygon from the front, the vertices will be ordered in counterclockwise direction. To add a back face we’ll create a second triangle with the inverse vertex order:
mesh {
// Front
polygon {
color red
point 0 0
color green
point 1 0
color blue
point 1 1
}
// Back
polygon {
point 1 1
point 1 0
point 0 0
}
}
(We’ve left the back face white in this case, so you can tell which side is which). If you get info again you’ll see that the triangle is now watertight:
We’ve learned how to make a triangle, but what about something a bit more complex? For our next trick we’re going to create a cube.
A cube has six faces, each consisting of four vertices - 24 vertices in total. But in fact there are really only eight unique vertices, so rather than tediously typing out the same points over and over, maybe we can use the power of scripting to save ourselves some effort?
First we’ll define a block called “side” that creates a single side of the cube:
define side {
color rnd rnd rnd
polygon {
point -0.5 -0.5 0.5
point +0.5 -0.5 0.5
point +0.5 +0.5 0.5
point -0.5 +0.5 0.5
}
}
Note that the color rnd rnd rnd
- this will produce a different random color each time the block is called, making the cube a little more interesting to look at.
Next we’ll call side
block six times from inside a mesh
, passing a different orientation each time:
mesh {
side { orientation 0 }
side { orientation 0 0.5 }
side { orientation 0 1 }
side { orientation 0 1.5 }
side { orientation 0 0 0.5 }
side { orientation 0 0 -0.5 }
}
And voila! A colored cube:
Obviously this is a lot more trouble than just using the cube command, but hopefully you can see the potential for creating more interesting shapes.
In many 3D engines, meshes must be constructed exclusively from triangles. The reason for this is that by definition a triangle is always both convex and planar, meaning that all its vertices lie on the same plane, which simplifies the mathematics needed to rasterize or render the mesh.
ShapeScript places no such restrictions on the polygons that you define in your mesh. Non-planar or non-convex polygons will be automatically split into their constituent triangles as needed for export or display.