Let's have some fun with EuRaylib

new topic     » topic index » view thread      » older message » newer message

Andys EuRaylib 6 ( even though it says 5). These are each separate programs, make sure you have raylib setup, Eu32 bit, everything else is standard Eu.

-- random_pixels.ex 
-- Draws random pixels in random colours on an 800x600 window 
-- Requires: 32-bit Euphoria 4.x and Euraylib 5 (Andy P / Icy Viking) 
 
include std/rand.e 
include raylib.e 
 
constant SCREEN_W = 800 
constant SCREEN_H = 600 
constant PIXELS_PER_FRAME = 10000 
 
set_rand(time())   -- seed the random number generator 
 
InitWindow(SCREEN_W, SCREEN_H, "Euraylib 5 - Random Pixels") 
SetTargetFPS(60) 
 
while not WindowShouldClose() do 
 
    BeginDrawing() 
    ClearBackground(BLACK) 
 
    for i = 1 to PIXELS_PER_FRAME do 
        -- random position (raylib screen coords start at 0) 
        integer x = rand(SCREEN_W) - 1 
        integer y = rand(SCREEN_H) - 1 
 
        -- random colour {r, g, b, a} 
        sequence colour = {rand(256) - 1, rand(256) - 1, rand(256) - 1, 255} 
 
        DrawPixel(x, y, colour) 
    end for 
 
    EndDrawing() 
 
end while 
 
CloseWindow() 
 
-- lines.ex 
-- Lines screensaver: 1024x800 window, bouncing line with fading trail 
-- Requires: 32-bit Euphoria 4.x and Euraylib 5 (Andy P / Icy Viking) 
 
include std/rand.e 
include std/math.e 
include raylib.e 
 
constant SCREEN_W = 1024 
constant SCREEN_H = 800 
constant TRAIL_LENGTH = 80      -- how many old lines stay on screen 
constant MAX_SPEED = 7 
 
set_rand(time()) 
 
-- p = {x1, y1, x2, y2}  : the two endpoints of the current line 
sequence p = { 
    rand(SCREEN_W) - 1, rand(SCREEN_H) - 1, 
    rand(SCREEN_W) - 1, rand(SCREEN_H) - 1 
} 
 
-- v = velocity of each coordinate, random speed and direction 
sequence v = repeat(0, 4) 
for i = 1 to 4 do 
    v[i] = rand_range(2, MAX_SPEED) * (rand(2) * 2 - 3)   -- (rand(2)*2-3) is -1 or +1 
end for 
 
sequence history = {}     -- each entry: {x1, y1, x2, y2, r, g, b} 
atom hue = 0              -- drives the colour cycling 
 
InitWindow(SCREEN_W, SCREEN_H, "Euraylib 5 - Lines") 
SetTargetFPS(60) 
 
while not WindowShouldClose() do 
 
    -- move the endpoints, bouncing off the edges 
    for i = 1 to 4 do 
        integer limit 
        if remainder(i, 2) = 1 then 
            limit = SCREEN_W - 1   -- x coordinates 
        else 
            limit = SCREEN_H - 1   -- y coordinates 
        end if 
 
        p[i] += v[i] 
 
        if p[i] < 0 then 
            p[i] = 0 
            v[i] = -v[i] 
        elsif p[i] > limit then 
            p[i] = limit 
            v[i] = -v[i] 
        end if 
    end for 
 
    -- smoothly cycle the colour 
    hue += 0.02 
    integer r = floor(127.5 + 127.5 * sin(hue)) 
    integer g = floor(127.5 + 127.5 * sin(hue + 2.094)) 
    integer b = floor(127.5 + 127.5 * sin(hue + 4.188)) 
 
    -- add this frame's line to the trail, drop the oldest if it's full 
    history = append(history, {p[1], p[2], p[3], p[4], r, g, b}) 
    if length(history) > TRAIL_LENGTH then 
        history = history[2..$] 
    end if 
 
    BeginDrawing() 
    ClearBackground(BLACK) 
 
    -- draw oldest to newest, with newer lines more opaque 
    for i = 1 to length(history) do 
        sequence ln = history[i] 
        integer alpha = floor(255 * i / length(history)) 
        DrawLine(ln[1], ln[2], ln[3], ln[4], {ln[5], ln[6], ln[7], alpha}) 
    end for 
 
    EndDrawing() 
 
end while 
 
CloseWindow() 
-- cube.ex 
-- Rotating wireframe cube, 1024x800 window, colour-cycling edges 
-- Requires: 32-bit Euphoria 4.x and Euraylib 5 (Andy P / Icy Viking) 
 
include std/math.e 
include raylib.e 
 
constant SCREEN_W = 1024 
constant SCREEN_H = 800 
constant CENTRE_X = SCREEN_W / 2 
constant CENTRE_Y = SCREEN_H / 2 
 
constant CUBE_SIZE = 180      -- half the length of a cube edge, in pixels 
constant CAMERA_DIST = 700    -- distance from the "camera" to the cube centre 
constant FOCAL = 700          -- perspective strength 
 
-- the 8 corners of a cube centred on the origin 
constant verts = { 
    {-1, -1, -1}, { 1, -1, -1}, { 1,  1, -1}, {-1,  1, -1}, 
    {-1, -1,  1}, { 1, -1,  1}, { 1,  1,  1}, {-1,  1,  1} 
} 
 
-- the 12 edges, as pairs of indexes into verts 
constant edges = { 
    {1, 2}, {2, 3}, {3, 4}, {4, 1},     -- back face 
    {5, 6}, {6, 7}, {7, 8}, {8, 5},     -- front face 
    {1, 5}, {2, 6}, {3, 7}, {4, 8}      -- connecting edges 
} 
 
-- rotate a point around the X, then Y, then Z axis 
function rotate_point(sequence v, atom ax, atom ay, atom az) 
    atom x = v[1] 
    atom y = v[2] 
    atom z = v[3] 
    atom t 
 
    -- around X 
    t = y * cos(ax) - z * sin(ax) 
    z = y * sin(ax) + z * cos(ax) 
    y = t 
 
    -- around Y 
    t = x * cos(ay) + z * sin(ay) 
    z = -x * sin(ay) + z * cos(ay) 
    x = t 
 
    -- around Z 
    t = x * cos(az) - y * sin(az) 
    y = x * sin(az) + y * cos(az) 
    x = t 
 
    return {x, y, z} 
end function 
 
-- project a 3D point onto the 2D screen, returns {screen_x, screen_y} 
function project_point(sequence v) 
    atom scale = FOCAL / (CAMERA_DIST + v[3] * CUBE_SIZE) 
    integer sx = floor(CENTRE_X + v[1] * CUBE_SIZE * scale) 
    integer sy = floor(CENTRE_Y + v[2] * CUBE_SIZE * scale) 
    return {sx, sy} 
end function 
 
-- turn a hue angle (radians) into an {r, g, b, a} colour 
function hue_colour(atom h) 
    integer r = floor(127.5 + 127.5 * sin(h)) 
    integer g = floor(127.5 + 127.5 * sin(h + 2.094)) 
    integer b = floor(127.5 + 127.5 * sin(h + 4.188)) 
    return {r, g, b, 255} 
end function 
 
atom ax = 0 
atom ay = 0 
atom az = 0 
atom hue = 0 
 
InitWindow(SCREEN_W, SCREEN_H, "Euraylib 5 - Rotating Cube") 
SetTargetFPS(60) 
 
while not WindowShouldClose() do 
 
    -- advance the rotation angles (different speeds look more natural) 
    ax += 0.011 
    ay += 0.017 
    az += 0.007 
    hue += 0.03 
 
    -- rotate and project all 8 corners 
    sequence pts = repeat({0, 0}, length(verts)) 
    for i = 1 to length(verts) do 
        pts[i] = project_point(rotate_point(verts[i], ax, ay, az)) 
    end for 
 
    BeginDrawing() 
    ClearBackground(BLACK) 
 
    -- draw each edge, each with its own offset in the colour cycle 
    for i = 1 to length(edges) do 
        sequence a = pts[edges[i][1]] 
        sequence b = pts[edges[i][2]] 
        sequence col = hue_colour(hue + i * 0.5) 
 
        -- draw each line three times with 1-pixel offsets to make it thicker 
        DrawLine(a[1],     a[2],     b[1],     b[2],     col) 
        DrawLine(a[1] + 1, a[2],     b[1] + 1, b[2],     col) 
        DrawLine(a[1],     a[2] + 1, b[1],     b[2] + 1, col) 
    end for 
 
    EndDrawing() 
 
end while 
 
CloseWindow() 
-- solid_cube.ex 
-- Rotating solid cube, 1024x800 window, six differently coloured opaque faces 
-- Requires: 32-bit Euphoria 4.x and Euraylib 5 (Andy P / Icy Viking) 
 
include std/math.e 
include raylib.e 
 
constant SCREEN_W = 1024 
constant SCREEN_H = 800 
constant CENTRE_X = SCREEN_W / 2 
constant CENTRE_Y = SCREEN_H / 2 
 
constant CUBE_SIZE = 180      -- half the length of a cube edge, in pixels 
constant CAMERA_DIST = 700    -- distance from the "camera" to the cube centre 
constant FOCAL = 700          -- perspective strength 
 
-- the 8 corners of a cube centred on the origin 
constant verts = { 
    {-1, -1, -1}, { 1, -1, -1}, { 1,  1, -1}, {-1,  1, -1}, 
    {-1, -1,  1}, { 1, -1,  1}, { 1,  1,  1}, {-1,  1,  1} 
} 
 
-- the 6 faces, each as 4 indexes into verts (in order around the face) 
constant faces = { 
    {1, 2, 3, 4},   -- back 
    {5, 6, 7, 8},   -- front 
    {1, 4, 8, 5},   -- left 
    {2, 3, 7, 6},   -- right 
    {1, 2, 6, 5},   -- top 
    {4, 3, 7, 8}    -- bottom 
} 
 
-- one colour per face {r, g, b, a} 
constant face_colours = { 
    {230,  40,  40, 255},   -- red 
    { 40, 200,  60, 255},   -- green 
    { 50,  90, 240, 255},   -- blue 
    {245, 220,  40, 255},   -- yellow 
    {220,  60, 220, 255},   -- magenta 
    { 40, 220, 220, 255}    -- cyan 
} 
 
-- rotate a point around the X, then Y, then Z axis 
function rotate_point(sequence v, atom ax, atom ay, atom az) 
    atom x = v[1] 
    atom y = v[2] 
    atom z = v[3] 
    atom t 
 
    -- around X 
    t = y * cos(ax) - z * sin(ax) 
    z = y * sin(ax) + z * cos(ax) 
    y = t 
 
    -- around Y 
    t = x * cos(ay) + z * sin(ay) 
    z = -x * sin(ay) + z * cos(ay) 
    x = t 
 
    -- around Z 
    t = x * cos(az) - y * sin(az) 
    y = x * sin(az) + y * cos(az) 
    x = t 
 
    return {x, y, z} 
end function 
 
-- project a 3D point onto the 2D screen, returns {screen_x, screen_y} 
function project_point(sequence v) 
    atom scale = FOCAL / (CAMERA_DIST + v[3] * CUBE_SIZE) 
    integer sx = floor(CENTRE_X + v[1] * CUBE_SIZE * scale) 
    integer sy = floor(CENTRE_Y + v[2] * CUBE_SIZE * scale) 
    return {sx, sy} 
end function 
 
-- draw a filled triangle, fixing the vertex order so raylib always accepts it 
-- (raylib only draws triangles whose points run counter-clockwise on screen) 
procedure draw_tri(sequence a, sequence b, sequence c, sequence col) 
    atom cross = (b[1] - a[1]) * (c[2] - a[2]) - (b[2] - a[2]) * (c[1] - a[1]) 
    if cross > 0 then 
        DrawTriangle(a, c, b, col) 
    else 
        DrawTriangle(a, b, c, col) 
    end if 
end procedure 
 
atom ax = 0 
atom ay = 0 
atom az = 0 
 
-- the camera sits on the negative Z side, in the same units as the cube corners 
constant cam_z = -CAMERA_DIST / CUBE_SIZE 
 
InitWindow(SCREEN_W, SCREEN_H, "Euraylib 5 - Solid Cube") 
SetTargetFPS(60) 
 
while not WindowShouldClose() do 
 
    ax += 0.011 
    ay += 0.017 
    az += 0.007 
 
    -- rotate all 8 corners, then project them 
    sequence rotated = repeat({0, 0, 0}, length(verts)) 
    sequence pts = repeat({0, 0}, length(verts)) 
    for i = 1 to length(verts) do 
        rotated[i] = rotate_point(verts[i], ax, ay, az) 
        pts[i] = project_point(rotated[i]) 
    end for 
 
    BeginDrawing() 
    ClearBackground(BLACK) 
 
    for f = 1 to length(faces) do 
        sequence idx = faces[f] 
 
        -- face centre = average of its 4 rotated corners. 
        -- For a cube centred on the origin this is also the outward normal. 
        atom cx = 0 
        atom cy = 0 
        atom cz = 0 
        for k = 1 to 4 do 
            cx += rotated[idx[k]][1] 
            cy += rotated[idx[k]][2] 
            cz += rotated[idx[k]][3] 
        end for 
        cx /= 4 
        cy /= 4 
        cz /= 4 
 
        -- the face is visible if its normal points towards the camera: 
        -- dot(normal, camera_position - face_centre) > 0 
        atom facing = cx * (0 - cx) + cy * (0 - cy) + cz * (cam_z - cz) 
 
        if facing > 0 then 
            sequence p1 = pts[idx[1]] 
            sequence p2 = pts[idx[2]] 
            sequence p3 = pts[idx[3]] 
            sequence p4 = pts[idx[4]] 
 
            -- a quad is two triangles 
            draw_tri(p1, p2, p3, face_colours[f]) 
            draw_tri(p1, p3, p4, face_colours[f]) 
 
            -- thin black outline so the edges stand out 
            DrawLine(p1[1], p1[2], p2[1], p2[2], BLACK) 
            DrawLine(p2[1], p2[2], p3[1], p3[2], BLACK) 
            DrawLine(p3[1], p3[2], p4[1], p4[2], BLACK) 
            DrawLine(p4[1], p4[2], p1[1], p1[2], BLACK) 
        end if 
    end for 
 
    EndDrawing() 
 
end while 
 
CloseWindow() 
 

And finally, this blew my mind

-- enterprise.ex 
-- Rotating wireframe starship, 1024x800 window 
-- Requires: 32-bit Euphoria 4.x and Euraylib 5 (Andy P / Icy Viking) 
 
include raylib.e 
 
constant SCREEN_W = 1024 
constant SCREEN_H = 800 
constant CENTRE_X = SCREEN_W / 2 
constant CENTRE_Y = SCREEN_H / 2 
 
constant MODEL_SCALE = 150    -- pixels per model unit 
constant CAMERA_DIST = 1100   -- distance from the "camera" to the model centre 
constant FOCAL = 1100         -- perspective strength 
constant TWO_PI = 6.283185307179586   -- own constant, avoids clashing with PI in raylib.e 
 
------------------------------------------------------------------ 
-- Model building helpers 
------------------------------------------------------------------ 
sequence verts = {}                  -- each: {x, y, z} 
sequence edges = {}                  -- each: {vertex_a, vertex_b, {r, g, b}} 
sequence cur_col = {190, 205, 235}   -- colour given to new edges 
 
function add_v(atom x, atom y, atom z) 
    verts = append(verts, {x, y, z}) 
    return length(verts) 
end function 
 
procedure add_e(integer a, integer b) 
    edges = append(edges, {a, b, cur_col}) 
end procedure 
 
-- Spin a profile around an axis to make a ring-and-spoke surface. 
-- axis  : 2 = spin around Y (vertical), 3 = spin around Z (lengthwise) 
-- shape : list of {radius, position_along_axis}, in order along the axis 
-- A radius of 0 makes a single point (a nose or tail tip). 
-- spoke_step: draw a lengthwise/radial line every N segments 
procedure add_lathe(integer axis, atom cx, atom cy, atom cz, 
                    sequence shape, integer segs, integer spoke_step) 
    sequence prev = {} 
 
    for p = 1 to length(shape) do 
        atom r = shape[p][1] 
        atom along = shape[p][2] 
        sequence ring = repeat(0, segs) 
 
        if r = 0 then 
            if axis = 2 then 
                ring = repeat(add_v(cx, cy + along, cz), segs) 
            else 
                ring = repeat(add_v(cx, cy, cz + along), segs) 
            end if 
        else 
            for s = 1 to segs do 
                atom a = TWO_PI * (s - 1) / segs 
                atom u = r * cos(a) 
                atom w = r * sin(a) 
                if axis = 2 then 
                    ring[s] = add_v(cx + u, cy + along, cz + w) 
                else 
                    ring[s] = add_v(cx + u, cy + w, cz + along) 
                end if 
            end for 
 
            -- the ring itself 
            for s = 1 to segs do 
                add_e(ring[s], ring[remainder(s, segs) + 1]) 
            end for 
        end if 
 
        -- spokes joining this ring to the previous one 
        if p > 1 then 
            for s = 1 to segs by spoke_step do 
                if prev[s] != ring[s] then 
                    add_e(prev[s], ring[s]) 
                end if 
            end for 
        end if 
 
        prev = ring 
    end for 
end procedure 
 
------------------------------------------------------------------ 
-- Build the ship (nose points towards +Z) 
------------------------------------------------------------------ 
 
-- Saucer section: {radius, height}, top to bottom 
cur_col = {190, 205, 235} 
add_lathe(2, 0, 0, 0.9, 
    {{0.0, 0.17}, {0.25, 0.17}, {0.5, 0.13}, {0.8, 0.07}, {1.0, 0.0}, 
     {0.8, -0.06}, {0.5, -0.09}, {0.25, -0.1}, {0.0, -0.1}}, 
    32, 2) 
 
-- Bridge dome on top of the saucer 
cur_col = {255, 230, 140} 
add_lathe(2, 0, 0.17, 0.9, 
    {{0.0, 0.1}, {0.08, 0.07}, {0.12, 0.0}}, 
    12, 3) 
 
-- Secondary (engineering) hull: {radius, z} 
cur_col = {170, 185, 215} 
add_lathe(3, 0, -0.5, -0.4, 
    {{0.0, -0.9}, {0.1, -0.85}, {0.17, -0.7}, {0.17, 0.3}, 
     {0.12, 0.5}, {0.0, 0.6}}, 
    16, 4) 
 
-- Neck joining the secondary hull to the underside of the saucer 
integer n1 = add_v(-0.07, -0.345, -0.35) 
integer n2 = add_v( 0.07, -0.345, -0.35) 
integer n3 = add_v( 0.07, -0.345, -0.10) 
integer n4 = add_v(-0.07, -0.345, -0.10) 
integer t1 = add_v(-0.10, -0.07,  0.10) 
integer t2 = add_v( 0.10, -0.07,  0.10) 
integer t3 = add_v( 0.10, -0.095, 0.50) 
integer t4 = add_v(-0.10, -0.095, 0.50) 
add_e(n1, n2) 
add_e(n2, n3) 
add_e(n3, n4) 
add_e(n4, n1) 
add_e(t1, t2) 
add_e(t2, t3) 
add_e(t3, t4) 
add_e(t4, t1) 
add_e(n1, t1) 
add_e(n2, t2) 
add_e(n3, t3) 
add_e(n4, t4) 
 
-- Two nacelles with pylons (sx = -1 is one side, +1 is the other) 
for sx = -1 to 1 by 2 do 
    -- nacelle body 
    cur_col = {90, 170, 255} 
    add_lathe(3, sx * 0.7, 0.12, -0.75, 
        {{0.0, -0.78}, {0.08, -0.72}, {0.1, -0.6}, {0.1, 0.4}}, 
        12, 3) 
 
    -- glowing red front dome 
    cur_col = {255, 90, 70} 
    add_lathe(3, sx * 0.7, 0.12, -0.35, 
        {{0.1, 0.0}, {0.085, 0.12}, {0.05, 0.22}, {0.0, 0.3}}, 
        12, 3) 
 
    -- pylon from the secondary hull up to the nacelle 
    cur_col = {170, 185, 215} 
    integer pa = add_v(sx * 0.1, -0.36, -1.05) 
    integer pb = add_v(sx * 0.1, -0.36, -0.70) 
    integer pc = add_v(sx * 0.7,  0.03, -1.05) 
    integer pd = add_v(sx * 0.7,  0.03, -0.70) 
    add_e(pa, pb) 
    add_e(pc, pd) 
    add_e(pa, pc) 
    add_e(pb, pd) 
end for 
 
-- Centre the whole model on the origin so it spins about its middle 
sequence lo = {1e30, 1e30, 1e30} 
sequence hi = {-1e30, -1e30, -1e30} 
for i = 1 to length(verts) do 
    for k = 1 to 3 do 
        if verts[i][k] < lo[k] then lo[k] = verts[i][k] end if 
        if verts[i][k] > hi[k] then hi[k] = verts[i][k] end if 
    end for 
end for 
sequence mid = (lo + hi) / 2 
for i = 1 to length(verts) do 
    verts[i] -= mid 
end for 
 
------------------------------------------------------------------ 
-- 3D maths 
------------------------------------------------------------------ 
 
-- rotate a point around the X, then Y, then Z axis 
function rotate_point(sequence v, atom ax, atom ay, atom az) 
    atom x = v[1] 
    atom y = v[2] 
    atom z = v[3] 
    atom t 
 
    -- around X 
    t = y * cos(ax) - z * sin(ax) 
    z = y * sin(ax) + z * cos(ax) 
    y = t 
 
    -- around Y 
    t = x * cos(ay) + z * sin(ay) 
    z = -x * sin(ay) + z * cos(ay) 
    x = t 
 
    -- around Z 
    t = x * cos(az) - y * sin(az) 
    y = x * sin(az) + y * cos(az) 
    x = t 
 
    return {x, y, z} 
end function 
 
-- project a 3D point onto the screen (model +Y is up, screen Y is down) 
function project_point(sequence v) 
    atom scale = FOCAL / (CAMERA_DIST + v[3] * MODEL_SCALE) 
    integer sx = floor(CENTRE_X + v[1] * MODEL_SCALE * scale) 
    integer sy = floor(CENTRE_Y - v[2] * MODEL_SCALE * scale) 
    return {sx, sy} 
end function 
 
------------------------------------------------------------------ 
-- Main program 
------------------------------------------------------------------ 
atom ax = 0.4 
atom ay = 0 
atom az = 0 
 
InitWindow(SCREEN_W, SCREEN_H, "Euraylib 5 - Wireframe Starship") 
SetTargetFPS(60) 
 
while not WindowShouldClose() do 
 
    -- spin on all three axes at different speeds 
    ax += 0.011 
    ay += 0.017 
    az += 0.007 
 
    sequence rotated = repeat({0, 0, 0}, length(verts)) 
    sequence pts = repeat({0, 0}, length(verts)) 
    for i = 1 to length(verts) do 
        rotated[i] = rotate_point(verts[i], ax, ay, az) 
        pts[i] = project_point(rotated[i]) 
    end for 
 
    BeginDrawing() 
    ClearBackground(BLACK) 
 
    for i = 1 to length(edges) do 
        integer ia = edges[i][1] 
        integer ib = edges[i][2] 
        sequence c = edges[i][3] 
 
        -- dim edges that are further from the camera (+Z is away from us) 
        atom zavg = (rotated[ia][3] + rotated[ib][3]) / 2 
        atom f = 0.65 - 0.35 * zavg / 1.9 
        if f < 0.25 then f = 0.25 end if 
        if f > 1 then f = 1 end if 
 
        DrawLine(pts[ia][1], pts[ia][2], pts[ib][1], pts[ib][2], 
                 {floor(c[1] * f), floor(c[2] * f), floor(c[3] * f), 255}) 
    end for 
 
    EndDrawing() 
 
end while 
 
CloseWindow() 
 
 

All written with the help of claude in 1/2 an hours - it even corrected a mistake with duplicate PI in raylib.e and math.e.

Enjoy

Cheers

Chris

Edit - and then it made a suggestion which I couldn't resist

-- enterprise2.ex 
-- Wireframe starship (Constitution-class refit style) with starfield and keyboard control 
-- 1024x800 window 
-- Requires: 32-bit Euphoria 4.x and Euraylib 5 (Andy P / Icy Viking) 
-- 
-- Controls: 
--   Arrow keys : pitch (up/down) and yaw (left/right) 
--   Z / X      : roll 
--   SPACE      : turn the automatic spin on/off 
--   R          : reset the orientation 
 
include std/rand.e 
include raylib.e 
 
constant SCREEN_W = 1024 
constant SCREEN_H = 800 
constant CENTRE_X = SCREEN_W / 2 
constant CENTRE_Y = SCREEN_H / 2 
 
constant MODEL_SCALE = 150    -- pixels per model unit 
constant CAMERA_DIST = 1100   -- distance from the "camera" to the model centre 
constant FOCAL = 1100         -- perspective strength 
constant TWO_PI = 6.283185307179586   -- own constant, avoids clashing with PI in raylib.e 
 
constant TURN_RATE = 0.03     -- radians per frame while a key is held 
 
-- starfield settings 
constant NUM_STARS = 300 
constant STAR_FAR = 1000      -- how far away new stars appear 
constant STAR_NEAR = 20       -- stars closer than this are recycled 
constant STAR_SPEED = 3       -- how fast the stars fly past 
constant STAR_FOCAL = 500 
 
set_rand(time()) 
 
------------------------------------------------------------------ 
-- Model building helpers 
------------------------------------------------------------------ 
sequence verts = {}                  -- each: {x, y, z} 
sequence edges = {}                  -- each: {vertex_a, vertex_b, {r, g, b}} 
sequence cur_col = {190, 205, 235}   -- colour given to new edges 
 
function add_v(atom x, atom y, atom z) 
    verts = append(verts, {x, y, z}) 
    return length(verts) 
end function 
 
procedure add_e(integer a, integer b) 
    edges = append(edges, {a, b, cur_col}) 
end procedure 
 
-- Spin a profile around an axis to make a ring-and-spoke surface. 
-- axis  : 2 = spin around Y (vertical), 3 = spin around Z (lengthwise) 
-- shape : list of {radius, position_along_axis}, in order along the axis 
-- A radius of 0 makes a single point (a nose or tail tip). 
-- A shape with just one entry makes a single ring (used for accent lines). 
-- spoke_step: draw a lengthwise/radial line every N segments 
procedure add_lathe(integer axis, atom cx, atom cy, atom cz, 
                    sequence shape, integer segs, integer spoke_step) 
    sequence prev = {} 
 
    for p = 1 to length(shape) do 
        atom r = shape[p][1] 
        atom along = shape[p][2] 
        sequence ring = repeat(0, segs) 
 
        if r = 0 then 
            if axis = 2 then 
                ring = repeat(add_v(cx, cy + along, cz), segs) 
            else 
                ring = repeat(add_v(cx, cy, cz + along), segs) 
            end if 
        else 
            for s = 1 to segs do 
                atom a = TWO_PI * (s - 1) / segs 
                atom u = r * cos(a) 
                atom w = r * sin(a) 
                if axis = 2 then 
                    ring[s] = add_v(cx + u, cy + along, cz + w) 
                else 
                    ring[s] = add_v(cx + u, cy + w, cz + along) 
                end if 
            end for 
 
            -- the ring itself 
            for s = 1 to segs do 
                add_e(ring[s], ring[remainder(s, segs) + 1]) 
            end for 
        end if 
 
        -- spokes joining this ring to the previous one 
        if p > 1 then 
            for s = 1 to segs by spoke_step do 
                if prev[s] != ring[s] then 
                    add_e(prev[s], ring[s]) 
                end if 
            end for 
        end if 
 
        prev = ring 
    end for 
end procedure 
 
------------------------------------------------------------------ 
-- Build the ship (nose points towards +Z) 
------------------------------------------------------------------ 
 
-- Primary hull (saucer): {radius, height}, top to bottom 
cur_col = {190, 205, 235} 
add_lathe(2, 0, 0, 0.9, 
    {{0.0, 0.20}, {0.15, 0.20}, {0.30, 0.17}, {0.45, 0.155}, {0.60, 0.12}, 
     {0.80, 0.08}, {0.95, 0.03}, {1.0, 0.0}, {0.95, -0.03}, {0.80, -0.07}, 
     {0.60, -0.09}, {0.40, -0.10}, {0.20, -0.11}, {0.0, -0.11}}, 
    40, 2) 
 
-- Window-light rings around the saucer edge 
cur_col = {255, 235, 150} 
add_lathe(2, 0, 0, 0.9, {{0.97, 0.025}}, 40, 1) 
add_lathe(2, 0, 0, 0.9, {{0.97, -0.025}}, 40, 1) 
 
-- Bridge dome on top of the saucer 
cur_col = {255, 230, 140} 
add_lathe(2, 0, 0.20, 0.9, 
    {{0.0, 0.09}, {0.06, 0.085}, {0.10, 0.05}, {0.13, 0.0}}, 
    12, 3) 
 
-- Sensor dome underneath the saucer 
cur_col = {120, 220, 255} 
add_lathe(2, 0, -0.11, 0.9, 
    {{0.12, 0.0}, {0.07, -0.05}, {0.0, -0.07}}, 
    12, 3) 
 
-- Secondary (engineering) hull: {radius, z} 
cur_col = {170, 185, 215} 
add_lathe(3, 0, -0.5, -0.4, 
    {{0.0, -0.9}, {0.08, -0.88}, {0.14, -0.80}, {0.17, -0.70}, {0.18, -0.40}, 
     {0.18, 0.0}, {0.17, 0.2}, {0.14, 0.35}, {0.10, 0.45}}, 
    16, 4) 
 
-- Hull accent rings 
cur_col = {255, 235, 150} 
add_lathe(3, 0, -0.5, -0.4, {{0.185, -0.55}}, 16, 1) 
add_lathe(3, 0, -0.5, -0.4, {{0.185, 0.15}}, 16, 1) 
 
-- Deflector dish at the front of the secondary hull 
cur_col = {120, 220, 255} 
add_lathe(3, 0, -0.5, -0.4, 
    {{0.10, 0.45}, {0.07, 0.42}, {0.03, 0.40}, {0.0, 0.395}}, 
    12, 3) 
 
-- Neck joining the secondary hull to the underside of the saucer 
cur_col = {170, 185, 215} 
integer n1 = add_v(-0.07, -0.33, -0.35) 
integer n2 = add_v( 0.07, -0.33, -0.35) 
integer n3 = add_v( 0.07, -0.33, -0.10) 
integer n4 = add_v(-0.07, -0.33, -0.10) 
integer t1 = add_v(-0.10, -0.07,  0.10) 
integer t2 = add_v( 0.10, -0.07,  0.10) 
integer t3 = add_v( 0.10, -0.10,  0.50) 
integer t4 = add_v(-0.10, -0.10,  0.50) 
add_e(n1, n2) 
add_e(n2, n3) 
add_e(n3, n4) 
add_e(n4, n1) 
add_e(t1, t2) 
add_e(t2, t3) 
add_e(t3, t4) 
add_e(t4, t1) 
add_e(n1, t1) 
add_e(n2, t2) 
add_e(n3, t3) 
add_e(n4, t4) 
 
-- Two nacelles with pylons (sx = -1 is one side, +1 is the other) 
for sx = -1 to 1 by 2 do 
    -- nacelle body 
    cur_col = {90, 170, 255} 
    add_lathe(3, sx * 0.7, 0.12, -0.75, 
        {{0.0, -0.78}, {0.05, -0.76}, {0.085, -0.70}, {0.10, -0.60}, 
         {0.10, 0.0}, {0.10, 0.40}}, 
        16, 4) 
 
    -- blue accent rings along the nacelle 
    cur_col = {120, 220, 255} 
    add_lathe(3, sx * 0.7, 0.12, -0.75, {{0.104, -0.35}}, 16, 1) 
    add_lathe(3, sx * 0.7, 0.12, -0.75, {{0.104, 0.05}}, 16, 1) 
 
    -- glowing red front dome (Bussard collector) 
    cur_col = {255, 90, 70} 
    add_lathe(3, sx * 0.7, 0.12, -0.35, 
        {{0.10, 0.0}, {0.09, 0.08}, {0.065, 0.17}, {0.035, 0.25}, {0.0, 0.30}}, 
        16, 4) 
 
    -- pylon from the secondary hull up to the nacelle 
    cur_col = {170, 185, 215} 
    integer pa = add_v(sx * 0.1, -0.36, -1.05) 
    integer pb = add_v(sx * 0.1, -0.36, -0.70) 
    integer pc = add_v(sx * 0.7,  0.03, -1.05) 
    integer pd = add_v(sx * 0.7,  0.03, -0.70) 
    add_e(pa, pb) 
    add_e(pc, pd) 
    add_e(pa, pc) 
    add_e(pb, pd) 
    add_e(pa, pd) 
end for 
 
-- Centre the whole model on the origin so it spins about its middle 
sequence lo = {1e30, 1e30, 1e30} 
sequence hi = {-1e30, -1e30, -1e30} 
for i = 1 to length(verts) do 
    for k = 1 to 3 do 
        if verts[i][k] < lo[k] then lo[k] = verts[i][k] end if 
        if verts[i][k] > hi[k] then hi[k] = verts[i][k] end if 
    end for 
end for 
sequence mid = (lo + hi) / 2 
for i = 1 to length(verts) do 
    verts[i] -= mid 
end for 
 
------------------------------------------------------------------ 
-- 3D maths (3x3 rotation matrices, so steering always feels the same 
-- no matter which way the ship is currently facing) 
------------------------------------------------------------------ 
 
function m3_mul(sequence a, sequence b) 
    sequence r = repeat(repeat(0, 3), 3) 
    for i = 1 to 3 do 
        for j = 1 to 3 do 
            r[i][j] = a[i][1] * b[1][j] + a[i][2] * b[2][j] + a[i][3] * b[3][j] 
        end for 
    end for 
    return r 
end function 
 
function m3_rotx(atom t) 
    return {{1, 0, 0}, {0, cos(t), -sin(t)}, {0, sin(t), cos(t)}} 
end function 
 
function m3_roty(atom t) 
    return {{cos(t), 0, sin(t)}, {0, 1, 0}, {-sin(t), 0, cos(t)}} 
end function 
 
function m3_rotz(atom t) 
    return {{cos(t), -sin(t), 0}, {sin(t), cos(t), 0}, {0, 0, 1}} 
end function 
 
function m3_apply(sequence m, sequence v) 
    return { 
        m[1][1] * v[1] + m[1][2] * v[2] + m[1][3] * v[3], 
        m[2][1] * v[1] + m[2][2] * v[2] + m[2][3] * v[3], 
        m[3][1] * v[1] + m[3][2] * v[2] + m[3][3] * v[3] 
    } 
end function 
 
-- project a 3D point onto the screen (model +Y is up, screen Y is down) 
function project_point(sequence v) 
    atom scale = FOCAL / (CAMERA_DIST + v[3] * MODEL_SCALE) 
    integer sx = floor(CENTRE_X + v[1] * MODEL_SCALE * scale) 
    integer sy = floor(CENTRE_Y - v[2] * MODEL_SCALE * scale) 
    return {sx, sy} 
end function 
 
------------------------------------------------------------------ 
-- Starfield 
------------------------------------------------------------------ 
function new_star(integer at_far_end) 
    integer z 
    if at_far_end then 
        z = STAR_FAR 
    else 
        z = rand_range(STAR_NEAR, STAR_FAR) 
    end if 
    return {rand_range(-1200, 1200), rand_range(-1000, 1000), z} 
end function 
 
sequence stars = repeat({0, 0, 0}, NUM_STARS) 
for i = 1 to NUM_STARS do 
    stars[i] = new_star(0) 
end for 
 
procedure draw_stars() 
    for i = 1 to NUM_STARS do 
        stars[i][3] -= STAR_SPEED 
 
        if stars[i][3] < STAR_NEAR then 
            stars[i] = new_star(1) 
        end if 
 
        integer sx = floor(CENTRE_X + stars[i][1] * STAR_FOCAL / stars[i][3]) 
        integer sy = floor(CENTRE_Y + stars[i][2] * STAR_FOCAL / stars[i][3]) 
 
        if sx < 0 or sx >= SCREEN_W - 1 or sy < 0 or sy >= SCREEN_H - 1 then 
            -- flew off the edge of the screen, send it back to the distance 
            stars[i] = new_star(1) 
        else 
            -- nearer stars are brighter 
            integer b = floor(255 * (1 - stars[i][3] / STAR_FAR)) 
            if b < 40 then b = 40 end if 
            sequence col = {b, b, b, 255} 
 
            DrawPixel(sx, sy, col) 
            if b > 170 then 
                -- the nearest stars are drawn as little 2x2 squares 
                DrawPixel(sx + 1, sy, col) 
                DrawPixel(sx, sy + 1, col) 
                DrawPixel(sx + 1, sy + 1, col) 
            end if 
        end if 
    end for 
end procedure 
 
------------------------------------------------------------------ 
-- Main program 
------------------------------------------------------------------ 
sequence orient = m3_rotx(0.4)    -- current orientation of the ship 
integer auto_spin = 1 
 
InitWindow(SCREEN_W, SCREEN_H, "Euraylib 5 - Wireframe Starship") 
SetTargetFPS(60) 
 
while not WindowShouldClose() do 
 
    -- ---- keyboard ---- 
    if IsKeyPressed(KEY_SPACE) then 
        auto_spin = not auto_spin 
    end if 
 
    if IsKeyPressed(KEY_R) then 
        orient = m3_rotx(0.4) 
    end if 
 
    atom dx = 0 
    atom dy = 0 
    atom dz = 0 
 
    if auto_spin then 
        dx = 0.011 
        dy = 0.017 
        dz = 0.007 
    end if 
 
    if IsKeyDown(KEY_UP)    then dx -= TURN_RATE end if 
    if IsKeyDown(KEY_DOWN)  then dx += TURN_RATE end if 
    if IsKeyDown(KEY_LEFT)  then dy -= TURN_RATE end if 
    if IsKeyDown(KEY_RIGHT) then dy += TURN_RATE end if 
    if IsKeyDown(KEY_Z)     then dz += TURN_RATE end if 
    if IsKeyDown(KEY_X)     then dz -= TURN_RATE end if 
 
    -- turn the ship a small step about the screen's X, Y and Z axes 
    orient = m3_mul(m3_rotz(dz), m3_mul(m3_roty(dy), m3_mul(m3_rotx(dx), orient))) 
 
    -- ---- transform the model ---- 
    sequence rotated = repeat({0, 0, 0}, length(verts)) 
    sequence pts = repeat({0, 0}, length(verts)) 
    for i = 1 to length(verts) do 
        rotated[i] = m3_apply(orient, verts[i]) 
        pts[i] = project_point(rotated[i]) 
    end for 
 
    -- ---- draw ---- 
    BeginDrawing() 
    ClearBackground(BLACK) 
 
    draw_stars() 
 
    for i = 1 to length(edges) do 
        integer ia = edges[i][1] 
        integer ib = edges[i][2] 
        sequence c = edges[i][3] 
 
        -- dim edges that are further from the camera (+Z is away from us) 
        atom zavg = (rotated[ia][3] + rotated[ib][3]) / 2 
        atom f = 0.65 - 0.35 * zavg / 1.9 
        if f < 0.25 then f = 0.25 end if 
        if f > 1 then f = 1 end if 
 
        DrawLine(pts[ia][1], pts[ia][2], pts[ib][1], pts[ib][2], 
                 {floor(c[1] * f), floor(c[2] * f), floor(c[3] * f), 255}) 
    end for 
 
    DrawText("Arrows: steer   Z/X: roll   SPACE: auto-spin   R: reset", 
             10, 10, 18, {150, 160, 185, 255}) 
 
    EndDrawing() 
 
end while 
 
CloseWindow() 
 

Try stabilising the tumbling ship with the arrow keys

new topic     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu