Pipe and Tunnel: experiments with meshes

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

Nice example of meshes. I like how you can go thru a tube and look at it from the inside. Is there anyway you can change the eye x,y,z location and viewing direction to allow the illusion that you’re moving along the inside of the tubes. It would be interesting to work your way along the inside of the tubes and see the inside of where they come together.

Hello @dave1707. Below is a variation - Tunnel - inspired by your suggestion. (Warning: this involves rapidly moving coloured elements.)

--
-- Tunnel
--
supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
function setup()
    local randomColours = true -- False for different shading option
    local pointNo = 30         -- No. of points in cross-section
    local radius = 30          -- Cross-section radius
    path = Pipe.rollerCoaster  -- Choose path for pipe
    speed = 1/20               -- Speed of animation

    light = vec3(1, 0, 0) -- Direction to light source
    ambient =0.25         -- Ambient light factor (0 to 1)
    local c = rCol()      -- A random colour
    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, 255)
                c2 = color(c.r*i1, c.g*i1, c.b*i1, 255)
            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()
    t = ElapsedTime * speed
    background(0) 
    local eye = eye(t) -- Get position of the eye
    local dir = eye + tangent(dt, path, t)
    pushMatrix()
    perspective() -- Set the default perspective
    camera(eye.x, eye.y, eye.z, -- Set eye location
        dir.x, dir.y, dir.z,    -- Set direction of eye
        0, 1, 0)                -- Set orientation of eye
    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(t)
    return path(t)
end

@mpilgrem

Fantastic. That’s what I had in mind. It makes me dizzy just watching it. Another great example of using meshes. I can’t tell, but are you following just one loop or is it going thru a different loop each time.

That’s great. Very fluid motion.

I put a half-tunnel track into the latest version of the roller coaster (so you can still see the stars).

Just posting a picture of your project, @mpilgrem, it’s very good.

Tunnel

Wow

Thank you for your kind words. I should emphasise the debt that this code owes to the existence of @Andrew_Stacey’s Roller Coaster example project, and to @dave1707’s encouragement.

I found quite interesting this prototype, i have changed the rCol() function to give a solid look to the tunnel, give it a try!!

-- Returns a random colour
red = math.random(255)
green = math.random(255)
blue = math.random(255)  
function rCol()
    local r = math.random(1,3)
    if r == 2 then
        inc = -1
    elseif r == 3 then
        inc = 1
    else 
        inc = 0
    end
    if inc~=0 then 
        red = morecolor(red,inc)
        blue = morecolor(blue,inc)
        green = morecolor(green,inc)
    end
    return color(red, green, blue)
end

function morecolor(c,inc)
    c = c + inc
    if c>255 or c<0 then
        c = math.random(255)
    end
    return c
end 

:slight_smile:

Your skills with meshes blow my mind @mpilgrem

That is fairly awesome, @mpilgrem. And in less than 150 lines of code, impressive.

Hello @juaxix. Did you mean the following (so that each circular ‘segment’ of the tunnel is, more a less, a single random colour)?

-- Returns a random colour
local red   = math.random(255)
local green = math.random(255)
local blue  = math.random(255)  
function rCol()
    local inc = math.random(1, 3)
    if r == 2 then
        inc = -1
    elseif r == 3 then
        inc = 1
    else 
        inc = 0
    end
    if inc~=0 then 
        red = morecolor(red,inc)
        blue = morecolor(blue,inc)
        green = morecolor(green,inc)
    end
    return color(red, green, blue)
end

function morecolor(c,inc)
    c = c + inc
    if c>255 or c<0 then
        c = math.random(255)
    end
    return c
end

That is certainly less disorienting than every triangle being a random colour!

Yes @mpilgrem ,red,green,blue as globals , i have just reedit the code.

A slightly different take - adding a texture to each pair of triangles.

--
-- Tunnel
--
supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
function setup()
    local randomColours = false -- False for different shading option
    local pointNo = 30         -- No. of points in cross-section
    local radius = 30          -- Cross-section radius
     red   = math.random(255)
 green = math.random(255)
 blue  = math.random(255)  
    path = Pipe.rollerCoaster  -- Choose path for pipe
    speed = 1/20               -- Speed of animation
    c=0
    light = vec3(1, 0, 0) -- Direction to light source
    ambient =0.25         -- Ambient light factor (0 to 1)
    local c = rCol()      -- A random colour
    dt = 1/400            -- No of cross-sections in pipe
    m = mesh()            -- Create a mesh
    m.texture="Cargo Bot:Command Grab"  --set a texture
    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)
            local c1, c2   
            c1 = rCol() -- A random colour
            c2=c1
        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, t3) -- First triangle
            table.insert(ver, t1)
            table.insert(ver, t2)
            table.insert(ver, t3) -- Second triangle
            table.insert(ver, t2)    
            table.insert(ver, t4)
            
            local idx = m:addRect(0,0,50,50) -- not sure what difference changing these values makes
            m:setRectTex(idx, 0, 0, 1, 1)
            
            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, 255)
                c2 = color(c.r*i1, c.g*i1, c.b*i1, 255)
            else
            --already taken care of
            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()
    t = ElapsedTime * speed
    background(0) 
    local eye = eye(t) -- Get position of the eye
    local dir = eye + tangent(dt, path, t)
    pushMatrix()
    perspective() -- Set the default perspective
    camera(eye.x, eye.y, eye.z, -- Set eye location
        dir.x, dir.y, dir.z,    -- Set direction of eye
        0, 1, 0)                -- Set orientation of eye
    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(t)
    return path(t)
end

@mpilgrem - absolutely brilliant! What about turning this into a game with a Fantastic Voyage (http://en.wikipedia.org/wiki/Fantastic_Voyage) theme? You just need to add some bacteria and virus to shoot at and blood cells to avoid.

Something along these lines?: http://www.youtube.com/watch?v=FC1NcwrS8NE&feature=youtube_gdata_player

Is not that a little dark, @West?

Increase the value of ambient to lighten @juaxix

Make randomColours true to get back to the more colourful version

These are part of @mpilgrem’s original posting

Thanks @West , i have not started yet with meshes, Btw, here is the video with the colors stuff

http://yfrog.us/n1gd9lnjbcxgdevjqutajonfz

Trippy