I have been experimenting with meshes with the following code, inspired by the Roller Coaster example project:
--
-- Pipe
--
supportedOrientations(LANDSCAPE_ANY)
function setup()
local randomColours = false -- True for different shading option
local pointNo = 30 -- No. of points in cross-section
local radius = 30 -- Cross-section radius
local path = Pipe.knot -- Choose path for pipe
parameter("r", 10, 1600, 1200)
parameter("theta", 0, 360, 0)
parameter("phi", 5, 175, 90) -- Avoid the discontinuity at poles
light = vec3(1, 0, 0) -- Direction to light source
ambient =0.25 -- Ambient light factor (0 to 1)
local c = rCol() -- A random colour
local dt = 1/400 -- No of cross-sections in pipe
m = mesh() -- Create a mesh
local ver = {} -- Table for vertices
local col = {} -- Table for their colours
for j = 0, 1 - dt, dt do -- For each cross-section in the pipe
p1 = circle(path(j),
tangent(dt, path, j), radius, pointNo)
p2 = circle(path(j+dt),
tangent(dt, path, j+dt), radius, pointNo)
for i = 1, pointNo do -- For each point in cross-section
local t1 = p1[i]
local t2 = p1[(i % pointNo)+1]
local t3 = p2[i]
local t4 = p2[(i % pointNo)+1]
table.insert(ver, t1) -- First triangle
table.insert(ver, t2)
table.insert(ver, t3)
table.insert(ver, t3) -- Second triangle
table.insert(ver, t2)
table.insert(ver, t4)
local c1, c2
if not randomColours then
local n1 = (t2 - t1):cross(t3 - t1)
n1 = n1:normalize()
local n2 = (t2 - t3):cross(t4 - t3)
n2 = n2:normalize()
local i1 = (math.max(n1:dot(light), 0) *
(1 - ambient) + ambient)
local i2 = (math.max(n2:dot(light), 0) *
(1 - ambient) + ambient)
c1 = color(c.r*i1, c.g*i1, c.b*i1)
c2 = color(c.r*i1, c.g*i1, c.b*i1)
else
c1 = rCol() -- A random colour
c2 = rCol() -- Another random colour
end
table.insert(col, c1) -- First triangle
table.insert(col, c1)
table.insert(col, c1)
table.insert(col, c2) -- Second triangle
table.insert(col, c2)
table.insert(col, c2)
end
end
m.vertices = ver -- Set vertices
m.colors = col -- Set their colours
end
function draw()
background(0)
local eye = eye(r, theta, phi) -- Get position of the eye
pushMatrix()
perspective() -- Set the default perspective
camera(eye.x, eye.y, eye.z, 0, 0, 0, 0, 1, 0) -- Set camera
m:draw() -- Draw mesh
popMatrix()
end
Pipe = {}
-- Function to define a path of the pipe
function Pipe.rollerCoaster(t)
local a = t * 2 * math.pi
local r = 300
local y = r/2 * math.sin(3*a)
local x = r * math.cos(a)
local z = r * math.sin(2*a)
return vec3(x, y, z)
end
-- Function to define a path of the pipe
function Pipe.knot(t)
local a = t * 2 * math.pi * 3
local r = 200 * (1 + math.cos(5 * a / 3))
local y = 100 * math.sin(5 * a / 3)
local x = r * math.cos(a)
local z = r * math.sin(a)
return vec3(x, y, z)
end
-- Function to estimate tangent of f(t) at t
function tangent(delta, f, t)
local u = t - delta/2
local v = t + delta/2
local df = f(v) - f(u)
return df:normalize()
end
-- Function returns points of next cross-section
local ov2 = nil
function circle(origin, dir, radius, pointNo)
local points = {}
local v1 = dir:cross(ov2 or vec3(1, 0, 0))
local v2 = dir:cross(v1)
v1 = v1:normalize()
v2 = v2:normalize()
ov2 = -v2
for i = 0, pointNo - 1 do
local a = i/pointNo * 2 * math.pi
local p = origin +
(v1 * math.cos(a) + v2 * math.sin(a)) * radius
table.insert(points, p)
end
return points
end
-- Returns a random colour
function rCol()
local red = math.random(255)
local green = math.random(255)
local blue = math.random(255)
return color(red, green, blue)
end
-- Returns position of eye
function eye(r, thetaDeg, phiDeg)
local deg2rad = math.pi/180 -- Conversion factor
local theta = thetaDeg * deg2rad
local phi = phiDeg * deg2rad
local x = r * math.cos(theta)*math.sin(phi)
local z = r * math.sin(theta)*math.sin(phi)
local y = r * math.cos(phi)
return vec3(x, y, z)
end