Let's have some fun with EuRaylib
- Posted by ChrisB (moderator) 2 days ago
- 135 views
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

