From c3b3fe59676417d11a0790bc93ce56b162989f87 Mon Sep 17 00:00:00 2001 From: inferrna Date: Sat, 18 Jan 2025 19:47:16 +0400 Subject: [PATCH 1/8] Lobster language support --- .gitmodules | 3 + grammars.yml | 2 + lib/linguist/languages.yml | 7 + samples/Lobster/bspgraphic.lobster | 41 ++++ samples/Lobster/collision.lobster | 46 ++++ samples/Lobster/cube.lobster | 150 +++++++++++++ .../Lobster/custom_shader_metaballs.lobster | 40 ++++ samples/Lobster/custom_shader_sobel.lobster | 52 +++++ samples/Lobster/dragtest.lobster | 43 ++++ samples/Lobster/dungeongen.lobster | 89 ++++++++ samples/Lobster/guitest.lobster | 66 ++++++ samples/Lobster/imguitest.lobster | 79 +++++++ samples/Lobster/lazy_py_triples.lobster | 69 ++++++ samples/Lobster/lobbytest.lobster | 134 ++++++++++++ samples/Lobster/lobstercraft.lobster | 91 ++++++++ samples/Lobster/mgtest.lobster | 198 +++++++++++++++++ samples/Lobster/pendulum.lobster | 68 ++++++ samples/Lobster/physics_water.lobster | 48 ++++ samples/Lobster/reach.lobster | 57 +++++ samples/Lobster/textinput.lobster | 36 +++ samples/Lobster/threads.lobster | 39 ++++ samples/Lobster/vonkoch.lobster | 44 ++++ samples/Lobster/vrtest.lobster | 126 +++++++++++ .../Lobster/wave_function_collapse.lobster | 94 ++++++++ samples/Lobster/will_it_shuffle.lobster | 205 ++++++++++++++++++ vendor/README.md | 1 + vendor/grammars/lobster_ling | 1 + 27 files changed, 1829 insertions(+) create mode 100644 samples/Lobster/bspgraphic.lobster create mode 100644 samples/Lobster/collision.lobster create mode 100644 samples/Lobster/cube.lobster create mode 100644 samples/Lobster/custom_shader_metaballs.lobster create mode 100644 samples/Lobster/custom_shader_sobel.lobster create mode 100644 samples/Lobster/dragtest.lobster create mode 100644 samples/Lobster/dungeongen.lobster create mode 100644 samples/Lobster/guitest.lobster create mode 100644 samples/Lobster/imguitest.lobster create mode 100644 samples/Lobster/lazy_py_triples.lobster create mode 100644 samples/Lobster/lobbytest.lobster create mode 100644 samples/Lobster/lobstercraft.lobster create mode 100644 samples/Lobster/mgtest.lobster create mode 100644 samples/Lobster/pendulum.lobster create mode 100644 samples/Lobster/physics_water.lobster create mode 100644 samples/Lobster/reach.lobster create mode 100644 samples/Lobster/textinput.lobster create mode 100644 samples/Lobster/threads.lobster create mode 100644 samples/Lobster/vonkoch.lobster create mode 100644 samples/Lobster/vrtest.lobster create mode 100644 samples/Lobster/wave_function_collapse.lobster create mode 100644 samples/Lobster/will_it_shuffle.lobster create mode 160000 vendor/grammars/lobster_ling diff --git a/.gitmodules b/.gitmodules index ae3766341..5edc56f40 100644 --- a/.gitmodules +++ b/.gitmodules @@ -875,6 +875,9 @@ [submodule "vendor/grammars/llvm.tmbundle"] path = vendor/grammars/llvm.tmbundle url = https://github.com/whitequark/llvm.tmbundle +[submodule "vendor/grammars/lobster"] + path = vendor/grammars/lobster + url = https://github.com/inferrna/lobster_ling.git [submodule "vendor/grammars/logos"] path = vendor/grammars/logos url = https://github.com/Cykey/Sublime-Logos diff --git a/grammars.yml b/grammars.yml index aaea7a180..02c7aa2ec 100644 --- a/grammars.yml +++ b/grammars.yml @@ -832,6 +832,8 @@ vendor/grammars/lisp.tmbundle: - source.lisp vendor/grammars/llvm.tmbundle: - source.llvm +vendor/grammars/lobster: +- source.lobster vendor/grammars/logos: - source.logos vendor/grammars/logtalk.tmbundle: diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml index bcba5cc18..713466140 100644 --- a/lib/linguist/languages.yml +++ b/lib/linguist/languages.yml @@ -4029,6 +4029,13 @@ LiveScript: codemirror_mode: livescript codemirror_mime_type: text/x-livescript language_id: 208 +Lobster: + type: programming + extensions: + - ".lobster" + ace_mode: python + tm_scope: source.lobster + language_id: 790066842 Logos: type: programming extensions: diff --git a/samples/Lobster/bspgraphic.lobster b/samples/Lobster/bspgraphic.lobster new file mode 100644 index 000000000..8372b33ee --- /dev/null +++ b/samples/Lobster/bspgraphic.lobster @@ -0,0 +1,41 @@ +import vec +import color +import gl + +let dirs = [ int2_x, int2_y ] + +def square(pos, size, depth) -> void: + if (rnd(4) or depth < 2) and size.x > 8 and size.y > 8: + var notfound = true + var dir = 0 + var split = 0 + while notfound: // FIXME + dir = 0 + split = rnd(size.x + size.y) + if split >= size.x: + split -= size.x + dir = 1 + if split >= 3 and split <= size[dir] - 3: + notfound = false + for(2) i: + let width = if i: size[dir] - split else: split + square(pos + dirs[dir] * split * i, dirs[dir] * width + dirs[1 - dir] * size[1 - dir], depth + 1) + else: + gl.translate pos: + gl.color(color_black) + gl.rect(float(size)) + let range = max(0.1, min(0.7, 1.0 - sqrt(size.x * size.y) / 250.0)) + let col = color { rnd_float(), rnd_float() * range + (1.0 - range), rnd_float(), 1.0 } * 0.15 + 0.75 + gl.color(col) + gl.rect(float(size - int2_1 * 2)) + +var seed = 342342432 + +fatal(gl.window("bsp", 512, 512)) + +while gl.frame(): + if gl.button("escape") == 1: return + if gl.button("space") == 1: seed += 675656 + gl.clear(color_black) + rnd_seed(seed) + square(int2_0, gl.window_size(), 0) diff --git a/samples/Lobster/collision.lobster b/samples/Lobster/collision.lobster new file mode 100644 index 000000000..310e56e6d --- /dev/null +++ b/samples/Lobster/collision.lobster @@ -0,0 +1,46 @@ +// Demonstrate the use of "circles_within_range", which is a simple function to implement +// fast collision or other algorithms that need to know about nearby objects. + +import std +import vec +import color +import gl + +let num_balls = 1000 // try 10000 ! +let ball_size = 1.0 / sqrt(num_balls) / 3.0 +let ball_speed = 0.001 +let push_force = 0.2 // Needs to be balanced with speed. + +let positions = map(num_balls): rnd_float2() +let radiuses = map(num_balls): (rnd_float() + 0.5) * ball_size +let dirs = map(num_balls): rnd_float2_norm() + +fatal(gl.window("collision", 1024, 1024)) + +while gl.frame(): + if gl.button("escape") == 1: return + gl.window_title("FPS: {(1.0 / gl.delta_time())}") + gl.clear(color_black) + gl.scale(float(gl.window_size().y)) + + // Move balls out of their own motivation. + for(num_balls) i: + positions[i] += dirs[i] * ball_speed + // Keep within bounds. + if not positions[i].x.in_range(1.0): dirs[i] *= float2 { -1.0, 1.0 } + if not positions[i].y.in_range(1.0): dirs[i] *= float2 { 1.0, -1.0 } + + // For each ball, find all nearby balls in an efficient manner. + let push_indices = circles_within_range(0.0, positions, radiuses, [], [], int2_0) + + // Now use this information to push other balls away. + for(push_indices) iv, j: + for(iv) i: + let v = positions[j] - positions[i] + let dist = magnitude(v) - radiuses[i] - radiuses[j] + positions[i] += -normalize(v) * push_force * -dist + + // Render them. + for(positions) p, i: + gl.translate p: + gl.circle(radiuses[i], 20) diff --git a/samples/Lobster/cube.lobster b/samples/Lobster/cube.lobster new file mode 100644 index 000000000..8292c8012 --- /dev/null +++ b/samples/Lobster/cube.lobster @@ -0,0 +1,150 @@ +// This example renders a cube with a different colorèd number on each face. +// Moving the mouse or WASD rotates the viewport around the cube like turning a +// globe in front of the camera. +// +// The example illustrates creating a simple mesh and rendering a texture on +// the mesh in various orientations. +// The example showcases the render_to_texture utility, which allows us to +// render a 2D drawing on the surfaces of a 3D object, using a frame buffer. +// The example also demostrates using gl.new_mesh, which allows us to orient +// the texture on each face of the cube, since the simpler gl.new_poly is not +// quite sufficient for this purpose. +// +// To generate the mesh for each face, cube_face_meshes uses the three least +// significant bits of ASCII characters to represent whether the edge is on the +// near or far side of the cube along each axis. +// +// -----ZYX CHR corner +// 00110000 '0' origin +// 00110001 '1' x +// 00110010 '2' y +// 00110011 '3' xy +// 00110100 '4' z +// 00110101 '5' xz +// 00110110 '6' yz +// 00110111 '7' zyz +// +// Each face of the cube contains four of the cube's vertices. +// The normal vector for the face of the cube must face outward to be opaque to +// an outside observer, so the vertices are listed counter-clockwise. +// In the following illustration, the interior faces are inverted, so the +// vertices appear in clockwise order from our perspective. +// +// The first index must be the top-right corner of the texture. +// The textures are arranged such that the textures are upright around the +// equator and the poles are connected top to bottom with their nearest +// neighbor. +// Rotating the vertex strings rotates the corresponding texture orientation. +// +// The faces are numbered according to the conventions of right-handed dice. +// All faces in opposition have the same sum. +// Numbers read counter-clockwise about the origin and its opposite vertex. +// +// inverted clockwise +// Z 4---5 .---. .---. +// \ |\ |\ 1540->2 |\ 2 \ | |\ 6<-5464 +// 0--X | 0---1 | .---. | 6 | . +// | | | | | 0462->3 |3| | | |4| 4<-5137 +// Y 6-|-7 | ' | 1 | '---' | +// \| \| 3102->1 \| | \ 5 \| 5<-7326 +// 2---3 '---' '---' +// counter-clockwise +// +// Kris Kowal + +import vec +import color +import gl +import texture +import camera + +fatal(gl.window("Lobster Cube", 515, 515)) +check(gl.set_font_name("data/fonts/Droid_Sans/DroidSans.ttf"), "can\'t load font") + +// cube_face_meshes contains the meshes for the six faces of a cube, +// facing outward. +let cube_face_vertices = [ + "3102", // 1 + "1540", // 2 + "0462", // 3 + "5137", // 4 + "7326", // 5 + "5764", // 6 +] + +// These are in order from top-right, counter-clockwise. +// I can offer no explanation why the origin is not the top- or bottom-left. +let square = [float2_0, float2_x, float2_1, float2_y] + +let cube_face_meshes = map(cube_face_vertices) v: + let positions = map(v) c: vec3_v(map(3) i: float(c & (1 << (2 - i)) != 0)) + let indices = [0, 1, 2, 2, 3, 0] + gl.new_mesh( + "PT", + positions, // "P" + [], // colors, + [], // normals, + square, // "T" texcoords, + [], // textcoords2, + indices + ) + +// Use the frame buffer to render a unique texture for each face of the cube, +// with its number. +// We use white on grey since we can use these as color multipliers where we +// render the mesh. +let detail = 256 +let cube_face_textures = map(6) i: + render_to_texture(nil, int2_1 * detail, false, nil, texture_format_nomipmap): + gl.ortho(true, false) + let label = "{i+1}" + gl.set_font_size(detail/2) + let size = gl.text_size(label) + gl.translate(float2_1 * float(detail) / 2.0 - float(size) / 2.0) + gl.clear(color_grey) + gl.color(color_white) + gl.text(label) + +// Colors are arranged such that CMY are about the origin and RGB on the polar +// opposites. +// Colors on opposite faces are also opposite hues. +let face_colors = [ + color_purple, // M + color_olive, // Y + color_teal, // C + color_dark_red, // R + color_dark_blue, // G + color_dark_green, // B +] + +// Rotate the camera to place the origin vertex of the cube in the center of +// the view. +let camera = Camera { float3_0, -45.0, 45.0 } + +// This demo is able to use camera.FPS_view but uses a different control model +// to move the camera about the origin at a fixed “elevation”. +def camera_GPS_update(upkey, leftkey, downkey, rightkey, elevation:float, mousesens:float, keyspeed:float): + let long = (gl.button(upkey) >= 1) - (gl.button(downkey) >= 1) + let lat = (gl.button(rightkey) >= 1) - (gl.button(leftkey) >= 1) + camera.pitch -= gl.mouse_delta(0).y / mousesens + long * keyspeed + camera.yaw -= gl.mouse_delta(0).x / mousesens - lat * keyspeed + camera.pitch = min(85.0, max(-85.0, camera.pitch)) + camera.position = vecfromyawpitch(camera.yaw, camera.pitch, -elevation, 0.0) + +while gl.frame() and not gl.button("escape"): + gl.clear(color_dark_grey) + gl.cursor(false) + gl.perspective(60.0, 0.1, 1000.0) + + camera_GPS_update("w", "a", "s", "d", 2.0, 4.0, 4.0) + camera.FPS_view() + + gl.translate(float3_1 / -2.0) + gl.set_shader("textured") + for(6) i: + // The texture colors are multiplied by the color in context. + // Since the texture on our mesh is white on black, we can change the + // white to a unique color for each face of the world. + gl.color(face_colors[i]) + gl.set_primitive_texture(0, cube_face_textures[i]) + gl.render_mesh(cube_face_meshes[i]) diff --git a/samples/Lobster/custom_shader_metaballs.lobster b/samples/Lobster/custom_shader_metaballs.lobster new file mode 100644 index 000000000..af2eb6986 --- /dev/null +++ b/samples/Lobster/custom_shader_metaballs.lobster @@ -0,0 +1,40 @@ +import vec +import color +import gl +import texture + +// Example of how to do run a custom shader on a full window rectangle. +// In this case, a simple metaballs example adapted from +// https://www.shadertoy.com/view/XssSzN + +fatal(gl.window("custom shader", 1024, 1024)) + +let mats = +""" +SHADER metaballs + VERTEX + INPUTS apos:4 atc:2 + UNIFORMS mvp + gl_Position = mvp * apos; + itc = atc; + PIXEL + INPUTS itc:2 + UNIFORM float time + vec2 uv = itc * 2.0 - 1.0; + float v = 0.0; + for (int i = 0; i < 100; i++) { + vec2 c = sin(time * (0.1 + float(i) / 300.0) + vec2(i, -i)); + v += 1.0 - smoothstep(0.0, 0.2, length(uv - c)); + } + frag_color = vec4(mix(vec3(v), vec3(1.0), smoothstep(0.9, 0.9, v)), 1.0); +""" + +fatal(gl.load_materials(mats, true)) + +while(gl.frame()): + if gl.button("escape") == 1: return + gl.clear(color_black) + gl.blend(blend_none) + gl.set_shader("metaballs") + gl.set_uniform("time", gl.time()) + gl.rect(float(gl.window_size())) diff --git a/samples/Lobster/custom_shader_sobel.lobster b/samples/Lobster/custom_shader_sobel.lobster new file mode 100644 index 000000000..2f4365abc --- /dev/null +++ b/samples/Lobster/custom_shader_sobel.lobster @@ -0,0 +1,52 @@ +import vec +import color +import gl +import texture + +// Example of how to do run a custom shader as a post-process on a scene. +// In this case, a simple sobel edge detection filter example adapted from +// https://www.shadertoy.com/view/4t3XDM + +fatal(gl.window("custom shader", 1024, 1024)) + +let mats = +""" +SHADER sobel + VERTEX + INPUTS apos:4 atc:2 + UNIFORMS mvp + gl_Position = mvp * apos; + itc = atc; + PIXEL + INPUTS itc:2 + UNIFORMS tex0 framebuffer_size + vec3 TL = texture(tex0, itc + vec2(-1, 1)/ framebuffer_size).rgb; + vec3 TM = texture(tex0, itc + vec2( 0, 1)/ framebuffer_size).rgb; + vec3 TR = texture(tex0, itc + vec2( 1, 1)/ framebuffer_size).rgb; + vec3 ML = texture(tex0, itc + vec2(-1, 0)/ framebuffer_size).rgb; + vec3 MR = texture(tex0, itc + vec2( 1, 0)/ framebuffer_size).rgb; + vec3 BL = texture(tex0, itc + vec2(-1, -1)/ framebuffer_size).rgb; + vec3 BM = texture(tex0, itc + vec2( 0, -1)/ framebuffer_size).rgb; + vec3 BR = texture(tex0, itc + vec2( 1, -1)/ framebuffer_size).rgb; + vec3 grad_x = -TL + TR - 2.0 * ML + 2.0 * MR - BL + BR; + vec3 grad_y = TL + 2.0 * TM + TR - BL - 2.0 * BM - BR; + frag_color.r = length(vec2(grad_x.r, grad_y.r)); + frag_color.g = length(vec2(grad_x.g, grad_y.g)); + frag_color.b = length(vec2(grad_x.b, grad_y.b)); +""" + +fatal(gl.load_materials(mats, true)) + +var tex = nil + +while(gl.frame()): + if gl.button("escape") == 1: return + // Render the body to a texture, then the post-process of it to + // the screen. + tex = post_process(tex, gl.window_size(), "sobel", false, + texture_format_nomipmap): + rnd_seed(0) + gl.clear(color_black) + for(256) i: + gl.translate rnd_float2() * (sincos(gl.time() * 10.0 + i) * 0.5 + 0.5) * float(gl.window_size()): + gl.circle(50.0, 7) diff --git a/samples/Lobster/dragtest.lobster b/samples/Lobster/dragtest.lobster new file mode 100644 index 000000000..0b05edc72 --- /dev/null +++ b/samples/Lobster/dragtest.lobster @@ -0,0 +1,43 @@ +// demonstrates how Lobster's event state can help program common operations without having to store state yourself + +import vec +import color +import gl + +fatal(gl.window("event animation and mouse drag test", 1024, 1024)) + +let lines = [] + +while gl.frame(): + + if gl.button("escape") == 1: return + + gl.clear(color_black) + gl.color(color_white) + + // allow user to drag a line, and store it when complete + // note how we can track a drag operation conveniently thanks to gl.last_pos + let start = float(gl.last_pos("mouse1", true)) + let cur = float(gl.mouse_pos(0)) + if magnitude(start - cur) > 1.0: // a click is not a drag + let down, up = gl.button("mouse1") + if down >= 1: // show feedback + gl.line(start, cur, 1.0) + if up == 1: // store line + lines.push([start, float(gl.last_pos("mouse1", false))]) + // note how we use the mouse up position, which may be != cur + + // draw previous lines + gl.color(color_light_grey) + for(lines) l: + gl.line(l[0], l[1], 1.0) + + // just for demonstration purposes, gl.last_time makes it easy to animate clicks etc. + // we show little animated circles on each mouse down or up + for(2) down: + gl.color(if down: color_green else: color_blue) + let clicktime = gl.time() - gl.last_time("mouse1", down) + if clicktime < 1.0: + gl.translate gl.last_pos("mouse1", down): + gl.line_mode 1: + gl.circle(clicktime * 20.0, 10) diff --git a/samples/Lobster/dungeongen.lobster b/samples/Lobster/dungeongen.lobster new file mode 100644 index 000000000..77f29dedd --- /dev/null +++ b/samples/Lobster/dungeongen.lobster @@ -0,0 +1,89 @@ +// based on: https://gist.github.com/munificent/b1bcd969063da3e6c298be070a22b604 +// via: https://gist.github.com/Joker-vD/cc5372a349559b9d1a3b220d5eaf2b01 + +import std +import vec +import color +import gl + +let TILE_VOID = ' ' +let TILE_FLOOR = '.' +let TILE_WALL = '#' +let TILE_CORNER = '!' +let TILE_OPEN_DOOR = '\'' +let TILE_CLOSED_DOOR = '+' +let TILE_PLAYER = '@' + +let fsize = int2 { 80, 40 } +let field = mapxy(fsize): TILE_VOID + +def cave(with_player): + // csize/start are all inner dimensions/coordinates (w/o walls) + let csize = rnd_int2(int2 { 10, 6 }) + int2 { 5, 3 } + let start = rnd_int2(fsize - csize - 2) + 1 + + // Cave iterator function that supplies wall/corner type. + def in_box(f): + forxy(csize + 2) v: + v += start - 1 + let at_wall = (v < start) + (v >= start + csize) + // We need to somehow record corners of all caves to check + // for intersections later, so we use a special tile for it + f(v, [ TILE_FLOOR, TILE_WALL, TILE_CORNER ][manhattan(at_wall)]) + + // Check if the new cave (with walls) intersects with the interior of + // any already existing cave. Touching walls/corners are okay + in_box() v: + if field[v] == TILE_FLOOR: return + + // Find a suitable place for a door + var door_counter = 0 + var door_pos = int2_0 + if not with_player: + in_box() v, tile: + // The door should not be created in the cave's corner or over + // another door, or in another cave's corner. It's impossible + // to make a cave without a door, because rnd always + // returns 0. + if tile == TILE_WALL and field[v] == TILE_WALL: + door_counter++ + if rnd(door_counter) == 0: door_pos = v + + // If the cave's walls were made completely out of corners + // and doors, don't make such a cave + if door_counter == 0: return + + // The cave looks okay, let's draw it. First, draw the walls and the floor + in_box() v, tile: + field[v] = tile + + // Now draw the door. + if door_counter > 0: + field[door_pos] = if rnd(2): TILE_OPEN_DOOR else: TILE_CLOSED_DOOR + + if with_player: + // A cave with the player has only the player inside it + field[rnd_int2(csize) + start] = TILE_PLAYER + else: + // A cave without the player has some random mobs and/or gold in it; + // 1d6 of entities total, 25% chance of gold, 75% of a mob. + // Mob letters range from 'A' to '~', inclusive + for rnd(6) + 1: + field[rnd_int2(csize) + start] = + if rnd(4) == 0: '$' else: 'A' + rnd('~' - 'A' + 1) + +rnd_seed(int(seconds_elapsed() * 1000000.0)) + +// A call to cave() is not guaranteed to actually make a new cave, +// so call it many times +for(1000) j: + cave(j == 0) + +// Remove special corner type. +forxy(fsize) v: + if field[v] == TILE_CORNER: + field[v] = TILE_WALL + +// Print the generated field +for(field) row: + print unicode_to_string(row) diff --git a/samples/Lobster/guitest.lobster b/samples/Lobster/guitest.lobster new file mode 100644 index 000000000..65c918164 --- /dev/null +++ b/samples/Lobster/guitest.lobster @@ -0,0 +1,66 @@ +import gui + +fatal(gl.window("gui test", 960, 640)) +check(gl.set_font_name("data/fonts/US101/US101.ttf"), "can\'t load font!") +//check(gl.set_font_name("C:\\Windows\\Fonts\\arialuni.ttf"), "can\'t load font!") + +let ghosttex = gl.load_texture("data/textures/lobster.jpg") +assert ghosttex + +var clicked = false +var focused = false + +while gl.frame(): + if gl.button("escape") == 1: + return + + let fontsize = gl.window_size().y / 10 + gl.set_font_size(fontsize, fontsize / 40.0) + + gl.clear(color_grey) + gl.color(color_white) + + gui.setclickcolors(color_dark_green, color_green) + + gui.start(gl.window_size(), 1, 1, true): + gui.hblock(1, 0): + //gui.fontscale(0.3): + // gui.text(unicode_to_string(map(500): _)) + + gui.text("A") + gui.fontscale(2): + gui.text("B") + gui.vblock(2, 0): + gui.text("1") + gui.hblock(1, 0.1): + gui.text("2") + gui.text("2") + gui.text("2") + gui.divider(2) + gui.text("3") + gui.text("4") + gui.space(0.2) + gui.divider(4) + gui.space(0.2) + gui.text("C") + gui.button(): + gui.border(int2_1 * 2): + gui.fontscale(2): + gui.text("clickme") + fn: + clicked = not clicked + fn: + focused = not focused + if clicked: + gui.color(color_red): + //gui.text(unicode_to_string([0x30E6, 0x30FC, 0x30B6, 0x30FC, 0x5225, 0x30B5, 0x30A4, 0x30C8])) + gui.text("ON") + gui.text(focused and "C" or "E") + gui.background(color_blue, false): + gui.margin(int2_1 * 4): + gui.text("D") + if(clicked): + gui.blend(2): + gui.image(float2_1, ghosttex) + gui.text("E") + gui.text("E") \ No newline at end of file diff --git a/samples/Lobster/imguitest.lobster b/samples/Lobster/imguitest.lobster new file mode 100644 index 000000000..af7fd6488 --- /dev/null +++ b/samples/Lobster/imguitest.lobster @@ -0,0 +1,79 @@ +// simple demo showing integration of https://github.com/ocornut/imgui + +import vec +import color +import gl +import imgui +import gl + +fatal(gl.window("imgui demo", 1280, 1024)) + +im.init(false, im.config_docking_enable, 3.0) +assert im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", 20.0) + +// The data we're editing: +class Thingy: + on = true + i = 1 + ratio = 0.5 + name = "Fred" + col = color_red + pos = float2_1 + fruits = ["apple", "banana", "pear"] + others:[Thingy] + windowflag = im.window_always_autoresize + +var d = Thingy { map(3): Thingy { [] } } +var demo = false +let foo = "bar" // Will show up in debug view. + +while gl.frame(): + gl.clear(color_grey) + + // <- Render game here. + + im.frame(): + // Maximum convenience: turn any data structure into editable UI! + im.window("Automatically created from a class", d.windowflag): + d = im.edit_anything(d) + + // Manually created widgets, so you can choose how to represent and layout data: + im.window("Created by code", im.window_no_collapse | im.window_always_autoresize): + im.tab_bar("tab_bar"): + im.tab("Widgets"): + im.text("A text!") + d.on = im.checkbox("Check it out!", d.on) + im.tooltip("This is worth checking out, really!") + d.ratio = im.sliderfloat("Float", d.ratio, 0.0, 1.0) + d.i = im.sliderint("Int", d.i, 0, 10) + d.name = im.input_text("Name", d.name) + d.i = im.radio(d.fruits, d.i, true) + d.i = im.combo("Combo", d.fruits, d.i) + d.i = im.listbox("ListBox", d.fruits, d.i, d.fruits.length) + d.col = im.coloredit("Color", d.col) + for(2) g: im.graph("Sin", map(20): sin(_ * 20.0) + 1.0, g) + if im.button("popup!"): + im.popup_open("pop1") + im.popup("pop1"): + im.selectable("select a") + im.selectable("select b") + im.tab("Debug"): + if not demo and im.button("show demo window"): demo = true + if im.button("Cause breakpoint() hit"): + // The debugger should have popped up! + // The program is frozen until you hit continue. + // Try putting these breakpoints anywhere in your own code. + breakpoint() + + // Show the built-in imgui demo window, for testing. + if demo: demo = im.window_demo() + + // Realtime debugging! Show all lobster variables as editable UI. + // Also show some engine stats, like e.g. a framerate graph. + // These are shown in realtime, if you want to pause and see local + // variables in functions, try breakpoint() above + im.window("Realtime Debugger", im.window_always_autoresize): + im.show_engine_stats() + im.treenode("Memory Usage"): + im.text(get_memory_usage(20)) + im.show_vars() diff --git a/samples/Lobster/lazy_py_triples.lobster b/samples/Lobster/lazy_py_triples.lobster new file mode 100644 index 000000000..ae7ac6db9 --- /dev/null +++ b/samples/Lobster/lazy_py_triples.lobster @@ -0,0 +1,69 @@ +/* Inpired by: https://aras-p.info/blog/2018/12/28/Modern-C-Lamentations/ + +You can't do "lazy evaluation" in most traditional languages, because you either can't abstract +over the end result (lack of lambdas, like in C), or you can't terminate the lazyness (can't +return from lambda, like in C++ and most other languages). + +C++ works around this by having chained iterable objects (ranges). +Haskell has lazy lists, but these require a "de-forestation" optimization to be efficient. + +And Lobster can do all of this easily and efficiently because.. you can actually return +out of lambdas to enclosing functions! +*/ + +// A generator that yields values indefinitely (well, it assumes you'll use "take" below +// before you run out of integer bits). +def ints(start, f): + while true: f(start++) + +// Version with an actual end. +def ints(start, end, f): + for(end - start + 1) i: f(start + i) + +// Generic way to only consume n values out of a lazy generator. +def take(n, gen): + var i = 0 + gen() f: + // This returns from take (and thus terminates all generators in between), which is not + // possible in most languages. + if i++ == n: return + f() + + +// Now we can do generic lazy "Pythagorean Triples", Lobster style: +def py_triples(n, f): + take(n) yield: + ints(1) z: + ints(1, z) x: + ints(x, z) y: + if x * x + y * y == z * z: + yield(): + f([ x, y, z ]) + +// Note that the language guarantees that lambdas get inlined (and so do named single use / small +// functions), so the above code mostly compiles to a bunch of nested loops, so this code should +// actually be efficient! + +// Also note this code is self contained, and fully statically typed. + +// Print the first 100. +py_triples(100) t: + print t + +/* Outputs: + +[3, 4, 5] +[6, 8, 10] +[5, 12, 13] +[9, 12, 15] +[8, 15, 17] +[12, 16, 20] +[7, 24, 25] +[15, 20, 25] +[10, 24, 26] +... +[65, 156, 169] +[119, 120, 169] +[26, 168, 170] + +*/ diff --git a/samples/Lobster/lobbytest.lobster b/samples/Lobster/lobbytest.lobster new file mode 100644 index 000000000..74987d107 --- /dev/null +++ b/samples/Lobster/lobbytest.lobster @@ -0,0 +1,134 @@ +import color +import imgui +import gl + +fatal(gl.window("lobby demo", 1280, 1024)) + +im.init(false, im.config_docking_enable, 3.0) +assert im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", 20.0) + +let LOBBY_MAX_MEMBERS = 4 + +let steamerr = steam.init(0, true, true) +if steamerr == 0: + print "Steam init failed" +elif steamerr < 0: + // Not started from Steam, exit this instance, Steam will launch again. + // This only happens if there wasn't a steam.appid.txt file. + print "Relaunching from Steam..." + return +else: + print "Welcome Steam user {steam.username()}" + +var new_key = "" +var new_value = "" + +def lobby_tree(label, lobby_ids, in_lobby): + im.treenode(label): + for(lobby_ids) id, idx: + let num_members = steam.lobby_get_num_members(id) + im.treenode("#{idx}: {id} ({num_members} members)"): + if in_lobby: + if im.button("Leave##{id}"): + steam.lobby_leave(id) + im.treenode("Modify:"): + new_key = im.input_text("Key", new_key) + new_value = im.input_text("Value", new_value) + if im.button("Add Data"): + steam.lobby_set_data(id, new_key, new_value) + new_key = "" + new_value = "" + im.treenode("Server:"): + let server_id = steam.lobby_get_game_server(id) + im.text("{server_id}") + else: + if im.button("Join##{id}"): + steam.lobby_join(id) + im.treenode("Data:"): + let keys, values = steam.lobby_get_all_data(id) + assert keys.length == values.length + for(keys.length) i: + if in_lobby: + if im.button("-##{id}_{i}"): + steam.lobby_delete_data(id, keys[i]) + im.same_line() + im.text("\"{keys[i]}\": \"{values[i]}\"") + if in_lobby: + let members = steam.lobby_get_members(id) + im.treenode("Members (#{members.length}):"): + for(members) m, i: + im.text("#{i}: {steam.friend_get_username(m)} ({m})") + +enum FilterType: + FT_Numerical + FT_String + FT_ResultCount +let filter_type_names = ["Numeric", "String", "Result Count"] + +enum CompareType: + CT_LTE = -2 + CT_LT = -1 + CT_EQ = 0 + CT_GT = 1 + CT_GTE = 2 + CT_NE = 3 +let compare_type_names = ["<=", "<", "==", ">", ">=", "!="] + +class Filter: + type: FilterType + key: string + int_value = 0 + string_value = "" + cmp = CT_EQ + +let filters = []::Filter + +while gl.frame(): + gl.clear(color_grey) + + im.frame(): + im.window("Lobbies", 0): + // Create Lobby + if im.button("Create Lobby"): + steam.lobby_create(LOBBY_MAX_MEMBERS) + let joined = steam.lobby_get_all_joined() + lobby_tree("Joined:", joined, true) + // Filters + if im.button("Add Filter"): + filters.push(Filter { FT_Numerical, "" }) + im.treenode("Filters:"): + var to_remove = -1 + for(filters) f, i: + im.treenode("#{i}"): + if im.button("Delete"): + to_remove = i + f.type = FilterType(im.combo("Type", filter_type_names, f.type)) + switch f.type: + case FT_Numerical: + f.key = im.input_text("Key", f.key) + f.int_value = im.input_int("Value", f.int_value, -10000, 10000) + f.cmp = CompareType(im.combo("Compare", compare_type_names, f.cmp - CT_LTE) + CT_LTE) + case FT_String: + f.key = im.input_text("Key", f.key) + f.string_value = im.input_text("Value", f.string_value) + f.cmp = CompareType(im.combo("Compare", compare_type_names, f.cmp - CT_LTE) + CT_LTE) + case FT_ResultCount: + f.int_value = im.input_int("Count", f.int_value, 1, 50) + if to_remove >= 0: + filters.remove(to_remove) + // Request Lobbies + if im.button("Request Lobbies"): + for(filters) f: + switch f.type: + case FT_Numerical: + steam.lobby_request_add_numerical_filter(f.key, f.int_value, f.cmp) + case FT_String: + steam.lobby_request_add_string_filter(f.key, f.string_value, f.cmp) + case FT_ResultCount: + steam.lobby_request_add_result_count_filter(f.int_value) + steam.lobby_request_list() + let matched = steam.lobby_request_get_lobbies() + if im.button("Refresh lobby data"): + for(matched) id: + steam.lobby_request_data(id) + lobby_tree("Matched:", matched, false) diff --git a/samples/Lobster/lobstercraft.lobster b/samples/Lobster/lobstercraft.lobster new file mode 100644 index 000000000..94abcf6c6 --- /dev/null +++ b/samples/Lobster/lobstercraft.lobster @@ -0,0 +1,91 @@ +// A minecraft clone in very few lines of code +// implements random world generation, chunks, rendering, and mining/building of blocks +// created in response to https://github.com/fogleman/Minecraft (which is 10x bigger in code) + +import std +import vec +import color +import gl +import texture +import camera + +fatal(gl.window("LobsterCraft(tm)", 1280, 800)) + +let lssize = int3 { 128, 128, 32 } +let csize = 16 +let inventory = []::int // blocks we've mined +let camera = Camera { float(lssize) / 2.0, 45.0, 0.0 } + +def inside(v): return all(v > 0) and all(v < lssize - 1) // keep the outer blocks empty for simplicity + +let cells = mapxyz(lssize) v: + // Generate blocks using noise. + let h = simplex(float(v) / float3 { 64.0, 64.0, 32.0 } + 11.0, 6, 1.0, 0.55) + // more likely to be solid the lower it is, and only when not on the outside: + let solid = h / 1.5 > div(v.z, lssize.z) - 0.5 and inside(v) + // pick material with noise too: + if solid: int((simplex(float(v) / float(lssize), 8, 3.0, 0.55) + 1.0) * 2.0) + 1 + else: 0 + +let colors = [ color_dark_red, color_olive, color_green, color_dark_grey, color_grey ] +let nbdirs = [ int3_x, -int3_x, int3_y, -int3_y, int3_z, -int3_z ] +let nbpolys = map([ "4576", "0231", "2673", "0154", "1375", "0462" ]) s: + map(s) c: vec3_v(map(3) i: float(c & (1 << (2 - i)) != 0)) +let tris = [ 0, 1, 2, 2, 3, 0 ] + +let meshes = mapxyz(lssize / csize): nil +def generate_mesh(ci): + let vpositions = [] + let vcolors = [] + let vnormals = [] + forxyz(int3_1 * csize) cv: // For all cells in a chunk + let v = ci * csize + cv + let e = cells[v] + guard e // if this cell is solid + for(nbdirs) nv, i: + guard not cells[nv + v] // and neighbor is empty + for(tris) ti: + vpositions.push(float(v) + nbpolys[i][ti]) + vcolors.push(colors[e - 1]) + vnormals.push(float(nv)) + meshes[ci] = gl.new_mesh("PCN", vpositions, vcolors, vnormals, [], [], []) +forxyz(lssize / csize) v: generate_mesh(v) + +while gl.frame(): + if gl.button("escape") == 1: return + gl.clear(color_light_blue) + gl.cursor(false) + gl.perspective(70.0, 0.1, 1000.0) + camera.FPS_update("w", "a", "s", "d", 10.0, 4.0, true) + camera.FPS_view() + gl.light(camera.position, float2 { 64.0, 0.25 }) + gl.blend(blend_none) + gl.set_shader("phong") + forxyz(lssize / csize) v: + gl.render_mesh(assert meshes[v]) + var first_solid = int3_1 * -1 // find the first solid and last empty block we're looking at + var last_empty = int3_1 * -1 + let camvec = camera.forward_vector() + for(100) i: // look at most 10 cubes ahead + let pos = int(camera.position + camvec * (i / 10.0)) + if inside(pos) and first_solid.x < 0: + if cells[pos]: first_solid = pos + else: last_empty = pos + // LMB places blocks and RMB removes blocks + if gl.button("mouse1") == 1 and last_empty.x >= 0 and inventory.length: + cells[last_empty] = inventory.pop() + if gl.button("mouse3") == 1 and first_solid.x >= 0: + inventory.push(cells[first_solid]) + cells[first_solid] = 0 + // Blindly regen chunk looked at each frame is the easiest way to deal with cross-chunk edits ;) + generate_mesh(first_solid / csize) + gl.set_shader("color") + gl.blend(blend_alpha) + let wh = 11.7 // let's add some water! + gl.color(color { 0.5, 0.5, 1.0, 0.5 }) + gl.polygon([ float3 { 1.0, 1.0, wh }, float3 { 1.0, lssize.y - 1.0, wh }, + float3 { lssize.x - 1.0, lssize.y - 1.0, wh }, float3 { lssize.x - 1.0, 1.0, wh } ]) + gl.ortho() + gl.color(color_white) + gl.translate gl.window_size() / 2: + gl.circle(5.0, 20) // simplified crosshairs diff --git a/samples/Lobster/mgtest.lobster b/samples/Lobster/mgtest.lobster new file mode 100644 index 000000000..358d1693f --- /dev/null +++ b/samples/Lobster/mgtest.lobster @@ -0,0 +1,198 @@ +// graphics demo showing Lobster's builtin facilities of generating meshes procedurally from implicit functions +// this functionality is under construction + +import vec +import color +import gl +import camera +import gl +import mg + +print "generating models... this may take some time!" + +fatal(gl.window("meshgen test (PLEASE WAIT...)", 1280, 800, gl.window_init_linear_color)) + +let camera = Camera { float3_1 * 10.0, 135.0, 0.0 } + +def mg.mirror_y(dist, body): + for(2) i: + mg.translate(float3 { 0.0, i * dist * 2.0 - dist, 0.0 }, body) + +//mg.set_color_noise(0.5, 1) +let aspoints = false +mg.set_point_mode(aspoints) +if not aspoints: + //mg.set_polygon_reduction(3, 0.98, 0.95) + mg.set_vertex_randomize(0.0) + +let density = 80 + +var rounded_cube = nil +if true: + mg.smooth(1.0) + mg.color(color_white) + mg.rotate(float3_1, 45): + mg.superquadric(float3_1 * 10.0, float3_1) + rounded_cube = mg.polygonize(density) + +var gun = nil +if true: + mg.smooth(0.5) + mg.color(color_white) + mg.rotate(float3_y, 90.0): + mg.scale_vec(float3 { 1.0, 1.3, 1.0 }): + mg.supertoroid(3.0, float3 { 3.0, 3.0, 5.0 }) + mg.translate(float3 { 0.0, -1.3, 2.0 }): + mg.rotate(float3_x, 20.0): + mg.superquadric(float3 { 3.0, 3.0, 3.0 }, float3 { 0.5, 0.5, 3.5 }) + mg.translate(float3 { 0.0, -8.0, 4.0 }): + mg.rotate(float3_x, 30.0): + mg.superquadric(float3 { 3.0, 3.0, 3.0 }, float3 { 0.5, 0.5, 2.5 }) + mg.translate(float3 { 0.0, -5.5, -3.0 }): + mg.rotate(float3_x, -20.0): + mg.superquadric(float3 { 5.0, 5.0, 100.0 }, float3 { 1.5, 2.5, 6.5 }) + mg.translate(float3 { 0.0, 2.0, 4.2 }): + mg.superquadric(float3 { 3.0, 100.0, 3.0 }, float3 { 2.0, 10.0, 3.0 }) + mg.translate(float3 { 0.0, 14.0, 5.0 }): + mg.rotate(float3_x, 90.0): + mg.cylinder(1.0, 3.0) + gun = mg.polygonize(density) + +var spaceship = nil +if true: + mg.smooth(0.5) + mg.color(color_white) + mg.superquadric_non_uniform(float3 { 1.0, 2.0, 2.0 }, float3 { 2.0, 2.0, 2.0 }, + float3 { 2.0, 3.5, 0.3 }, float3 { 0.5, 3.5, 0.3 }) + mg.superquadric_non_uniform(float3 { 2.0, 1.5, 1.0 }, float3 { 2.0, 1.5, 2.0 }, + float3 { 4.0, 1.0, 0.7 }, float3 { 0.7, 1.0, 0.4 }) + mg.translate(float3 { 1.0, 0.0, 0.4 }): + mg.superquadric_non_uniform(float3 { 2.0, 2.0, 1.0 }, float3 { 2.0, 2.0, 2.0 }, + float3 { 1.0, 0.5, 0.4 }, float3 { 0.5, 0.5, 0.5 }) + mg.mirror_y(1.5): + mg.translate(float3_x): + mg.superquadric(float3 { 100.0, 2.0, 2.0 }, float3 { 1.3, 0.2, 0.2 }) + mg.mirror_y(2): + mg.translate(float3_x): + mg.superquadric(float3 { 100.0, 2.0, 2.0 }, float3 { 1.0, 0.15, 0.15 }) + mg.mirror_y(1): + mg.translate(float3_x * -0.6): + mg.superquadric_non_uniform(float3 { 1.0, 2.0, 2.0 }, float3 { 1.0, 2.0, 2.0 }, + float3 { 1.5, 0.5, 0.5 }, float3 { 0.01, 0.5, 0.5 }) + mg.color(color { 1.0, 1.0, 1.0, 0.0 /* carve */ }): + mg.rotate(float3_y, 90.0): + mg.smooth(0.0): + mg.cylinder(0.35, 0.25) + spaceship = mg.polygonize(density * 2) + +def model_tree(numb, branchl, narrowf, leafs, leafc) -> void: + if numb: + mg.translate(float3_z * branchl): + mg.tapered_cylinder(1.0, narrowf, branchl) + mg.translate(float3_z * branchl): + let axis = float3(sincos(rnd(360)), 0.0) + let branches = rnd(3) + 1 + for(branches) i: + mg.rotate(float3_z, 360 * i / branches): + mg.rotate(axis, 12 * branches): + mg.scale(narrowf): + mg.sphere(1.0) + model_tree(numb - 1, branchl, narrowf, leafs, leafc) + else: + mg.color(leafc): + mg.sphere(leafs) + +let trees = [] + +if true: + mg.smooth(0.5) + mg.color(color { 0.6, 0.5, 0.4, 1.0 }) + model_tree(10, 1.5, 0.75, 12, color { 0.4, 1.0, 0.4, 1.0 }) + trees.push(mg.polygonize(density)) + + mg.smooth(0.5) + mg.color(color { 0.4, 0.3, 0.2, 1.0 }) + model_tree(10, 1.2, 0.8, 1.2, color { 0.6, 1.0, 0.3, 1.0 }) + trees.push(mg.polygonize(density)) + + mg.smooth(0.5) + mg.color(color { 0.5, 0.4, 0.3, 1.0 }) + model_tree(9, 2.0, 0.7, 15, color { 0.4, 0.6, 0.4, 1.0 }) + trees.push(mg.polygonize(density)) + +var landscape = nil +// Currently expensive way to do a landscape. +if true: + mg.smooth(0.1) + mg.color(color { 0.6, 0.5, 0.4, 1.0 }) + mg.landscape(0.5, 1.0) + landscape = mg.polygonize(density) + +if gun: print "vertcount: gun {gl.mesh_size(gun)}" +if spaceship: print "vertcount: ship {gl.mesh_size(spaceship)}" +for trees: print("vertcount: tree1 {gl.mesh_size(_)}") +if rounded_cube: print "vertcount: rounded_cube {gl.mesh_size(rounded_cube)}" + +print seconds_elapsed() + +while gl.frame(): + if gl.button("escape") == 1: return + + gl.window_title("meshgen test: {gl.delta_time()}") + + gl.clear(color_darkest_grey) + + let fovscale = 1.5 + + if true: + gl.cursor(false) + gl.perspective(60.0, 0.1, 1000.0) + + var speed = 10 + if gl.button("left shift") >= 1: speed = 50 + camera.FPS_update("w", "a", "s", "d", speed, 4.0, true) + camera.FPS_view() + gl.light(camera.position, float2 { 128.0, 0.1 }) + else: + gl.ortho3d(float3_0, float3 { gl.window_size().x, gl.window_size().y, 2000 } / 100) + gl.rotate_x(sincos(45)) + gl.rotate_z(sincos(45)) + gl.light(float3(sincos(gl.time() * 10), 0) * 100 + float3_z * 300, float2 { 128.0, 0.1 }) + + gl.blend 0: + gl.set_shader(aspoints and "phong_particle" or "flat") + + if spaceship: + gl.translate(float2_1 * 10.0): + gl.point_scale(1.0 * fovscale) + gl.render_mesh(spaceship) + + if gun: + gl.translate(float2_1 * 20.0): + gl.scale(0.2): + gl.point_scale(0.2 * fovscale) + gl.render_mesh(gun) + + if landscape: + gl.translate(float2_y * 30.0): + gl.scale(10): + gl.point_scale(10.0 * fovscale) + gl.render_mesh(landscape) + + if rounded_cube: + gl.translate(float2_y * 10.0): + gl.scale(2): + gl.point_scale(3.0 * fovscale) + gl.render_mesh(rounded_cube) + + for(10) j: + gl.translate(float3_y * j * -5): + for(trees) t, i: + gl.translate(float3_x * i * 10.0): + gl.scale(0.5): + gl.point_scale(0.5 * fovscale) + gl.render_mesh(t) + + gl.set_shader("color") + gl.debug_grid(int3 { 20, 20, 0 }, float3_1, 0.005) + diff --git a/samples/Lobster/pendulum.lobster b/samples/Lobster/pendulum.lobster new file mode 100644 index 000000000..9c5ed00f2 --- /dev/null +++ b/samples/Lobster/pendulum.lobster @@ -0,0 +1,68 @@ +// based on: http://www.physicsandbox.com/projects/double-pendulum.html + +import std +import vec +import color +import gl +import texture + +let g = 9.8 +let mass = 1 +let max_steps = 2000 + +class link: + len:float + dtheta = 0.0 + theta:float + + def ddl(): return dtheta * dtheta * len + +class pendulum: + links:[link] + pts = []::float2 + col:color + + def update(time): + let sc = sincos(links[0].theta - links[1].theta) + for(links) l, i: + let o = links[1 - i] + let d2theta = ((i + 1) * g * (sin(o.theta) * sc.x - (2.0 - i) * sin(l.theta)) + + (i * 2.0 - 1.0) * ((i + 1) * ddl(o) + ddl(l) * sc.x) * sc.y) / + (l.len * (2.0 - sc.x * sc.x)) + l.dtheta += d2theta * time + l.theta += degrees(l.dtheta * time) + + def render(): + gl.blend(blend_add): + for(pts) pos, i: + gl.color(col * i / max_steps) + gl.translate(pos): + gl.circle(10.0, 10) + + gl.color(color_white) + var pos = float2_0 + for(links) l: + let p = yx(sincos(l.theta)) * l.len + gl.line(float2_0, p, 2.0) + gl.translate(p) + gl.circle(4.0, 10) + pos += p + pts.push(pos) + if pts.length > max_steps: pts.remove(0) + +let pendula = map(5) i: + pendulum { + map(2): link { 100.0 + i * 25.0, 181.0 + i * 0.1 }, + color { 0.20 - i * 0.04, 0.075, i * 0.04, 1.0 } + } + +fatal(gl.window("double pendulum", 700, 700)) +while gl.frame(): + if gl.button("escape") == 1: return + gl.clear(color_black) + let time = gl.delta_time() * 4.0 + for(pendula) p: + gl.ortho() + gl.translate(float(gl.window_size()) / 2.0) + p.update(time) + p.render() diff --git a/samples/Lobster/physics_water.lobster b/samples/Lobster/physics_water.lobster new file mode 100644 index 000000000..4428dc84c --- /dev/null +++ b/samples/Lobster/physics_water.lobster @@ -0,0 +1,48 @@ +// Showing off physics features in Lobster + +import vec +import color +import gl +import texture +import camera +import physics +import gl + +fatal(gl.window("Physics demo : water", 1024, 768, gl.window_init_linear_color)) + +let worldsize = float2 { 60.0, 40.0 } + +ph.initialize(float2 { 0.0, -10.0 }) +ph.initialize_particles(0.15) +ph.set_shader(nil, "color_attr_particle") + +let floor = ph.create_box(float2 { 0.0, 1.0 }, float2 { 20.0, 1.0 }) +let wall1 = ph.create_box(float2 { -20.0, 7.0 }, float2 { 1.0, 7.0 }) +let wall2 = ph.create_box(float2 { 20.0, 7.0 }, float2 { 1.0, 7.0 }) + +floor.ph.set_color(color_dark_grey) +wall1.ph.set_color(color_dark_grey) +wall2.ph.set_color(color_dark_grey) + +let boxes = map 5: + let b = ph.create_box(float2 { 0.0, 10.0 + _ * 3.0 }, float2 { 1.0, 1.0 }) + b.ph.dynamic(true) + b + +ph.create_particle_circle(float2 { 0.0, 5.0 }, 7.0, color_red, ph.colormixingparticle) + +while gl.frame() and gl.button("escape") != 1: + gl.clear(color_black) + + // create right-handed coordinate system, with (0, 0) at the bottom middle + set_2d_worldspace(worldsize, float2 { 0.5, 1.0 }, float2 { 1.0, -1.0 }) + + if gl.button("mouse1") >= 1: + ph.create_particle_circle(gl.local_mouse_pos(0), 0.5, color_blue, ph.colormixingparticle) + + ph.step(gl.delta_time(), 8, 3) + + gl.blend(blend_add_alpha) + ph.render_particles(2.5) + gl.blend(blend_none) + ph.render() diff --git a/samples/Lobster/reach.lobster b/samples/Lobster/reach.lobster new file mode 100644 index 000000000..afdffa01f --- /dev/null +++ b/samples/Lobster/reach.lobster @@ -0,0 +1,57 @@ +// graphics demo showing very simple link based physics, based on a processing.js example + +import std +import vec +import color +import gl + +class segment: + pos:float2 + dir = float2_0 + +let segs = map 7: segment { float2 { 0.5, 1.0 } } + +let ballradius = 0.1 +var ball = float2_h +var ballvel = float2 { 0.005, -0.004 } + +fatal(gl.window("reach", 600, 600)) + +while gl.frame(): + if gl.button("escape") == 1: return + gl.clear(color_black) + + gl.scale(min(gl.window_size().x, gl.window_size().y)) + + ball += ballvel + + if(ball.x > 1.0 - ballradius or ball.x < ballradius): ballvel *= float2 { -1.0, 1.0 } + if(ball.y > 1.0 - ballradius or ball.y < ballradius): ballvel *= float2 { 1.0, -1.0 } + + gl.translate ball: + gl.color(color_dark_grey) + gl.circle(ballradius, 50) + gl.color(color_light_grey) + gl.circle(ballradius / 5.0, 20) + + var target = float2_0 + let seglength = 0.1 + + for(segs) seg, i: + let in = if i: target else: ball + seg.dir = normalize(in - seg.pos) * seglength + target = in - seg.dir + + reduce_reverse(segs) b, a: + b.pos = a.pos + a.dir + b + + for(segs) seg, i: + gl.translate seg.pos: + let segwidth = 0.01 + i * 0.005 + gl.color(color_white) + gl.line(float2_0, seg.dir, segwidth) + gl.translate(seg.dir) + gl.color(color_grey) + gl.circle(segwidth / 2.0 + 0.005, 20) + diff --git a/samples/Lobster/textinput.lobster b/samples/Lobster/textinput.lobster new file mode 100644 index 000000000..596658743 --- /dev/null +++ b/samples/Lobster/textinput.lobster @@ -0,0 +1,36 @@ +// Shows how you could input text strings. + +import std +import vec +import color +import gl + +gl.window("text input", 1200, 600) +check(gl.set_font_name("data/fonts/Inconsolata/Inconsolata-Regular.ttf"), "font!") + +gl.start_text_input(int2_0, int2_1 * gl.get_font_size()) + +let log = [] + +while gl.frame(): + gl.clear(color_black) + gl.set_font_size(30) + // This returns other values to render IME correctly, which we'll skip here. + let text = gl.text_input_state() + gl.text(text + "_") + if gl.button("return") == 1: + // Here we normally end the input. + log.insert(0, text) + gl.end_text_input() + // For now, go right back to inputting a new string: + gl.start_text_input(int2_0, int2_1 * gl.get_font_size()) + elif gl.key_repeat("backspace") == 1: + // Inefficient but simple way to drop last character. + let uv = string_to_unicode(text) + if uv.length: + uv.pop() + gl.set_text_input(unicode_to_string(uv)) + // Render all strings input so far: + for(log) l, i: + gl.translate(float2 { 0.0, gl.get_font_size() * (i + 2.0) }): + gl.text(l) diff --git a/samples/Lobster/threads.lobster b/samples/Lobster/threads.lobster new file mode 100644 index 000000000..a3ac24218 --- /dev/null +++ b/samples/Lobster/threads.lobster @@ -0,0 +1,39 @@ +// Simple multi-threading example. + +class fib_request: + attribute serializable + n:int + +class fib_response: + attribute serializable + fr:int + +def fib(n:int) -> int: + return if n <= 1: n else: fib(n - 1) + fib(n - 2) + +def time(name, f): + let start = seconds_elapsed() + f() + print "{name} took: {(seconds_elapsed() - start)}" + +if not is_worker_thread(): + let hwthreads, hwcores = thread_information() + print "running on {hwthreads} hardware threads and {hwcores} cores" + let fibn = 30 + let fibr = 832040 + time("single thread fib({fibn})"): assert fib(fibn) == fibr + // Let's launch some threads! + start_worker_threads(hwthreads) + let num_fibs = hwthreads + time("parallel fib({fibn}) * {num_fibs}"): + // Now Q up some work: + for num_fibs: thread_write(fib_request { fibn }) + // And wait for the responses. + for num_fibs: + let r = thread_read(typeof fib_response) + assert r and r.fr == fibr +else: + // We're a worker.. process jobs. + while workers_alive(): + let r = thread_read(typeof fib_request) + if r: thread_write(fib_response { fib(r.n) }) diff --git a/samples/Lobster/vonkoch.lobster b/samples/Lobster/vonkoch.lobster new file mode 100644 index 000000000..2e41d8a5b --- /dev/null +++ b/samples/Lobster/vonkoch.lobster @@ -0,0 +1,44 @@ +import color +import gl +import imgui +import vec + +// Sequence of the angles for the Von Koch curve's segment. Each angle +// represent a rotation to be done after the previous segment is drawn. +let seq = [ -60, 120, -60, 0 ] + +def curve(depth, d) -> void: + if depth <= 0: + // Draw a single segment and move to its end. + gl.line(float2 { 0.0, 0.0 }, float2 { d, 0.0 }, 0.002) + gl.translate(float2 { d, 0.0 }) + else: + for (seq) angle: + curve(depth - 1, d / 3.0) + gl.rotate_z(sincos(angle)) + +fatal(gl.window("Von Koch\'s curve", 640, 640)) +im.init(false) +assert im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", 20.0) + +class Setup: + depth: int + +var setup = Setup { 5 } + +while gl.frame(): + if gl.button("escape") == 1: return + gl.clear(color_white) + gl.color(color_blue) + gl.scale(gl.window_size().y * 0.75) + gl.translate(float2 { 0.1, 0.4 }) + + // Draw the Von koch's curve segments around a tringle + for (3): + curve(setup.depth, 1.0) + gl.rotate_z(sincos(120.0)) + + im.frame(): + im.window("Parameters", im.window_always_autoresize): + setup = im.edit_anything(setup) + diff --git a/samples/Lobster/vrtest.lobster b/samples/Lobster/vrtest.lobster new file mode 100644 index 000000000..dfa018bc9 --- /dev/null +++ b/samples/Lobster/vrtest.lobster @@ -0,0 +1,126 @@ +// graphics demo showing Lobster's built-in loading of (animated) .iqm models + +import vec +import color +import gl +import texture +import camera +import gl +import mg + +let vrmode = vr.init() + +fatal(gl.window(if vrmode: "VR Test" else: "No VR device", 1280, 640, + if vrmode: gl.window_init_no_vsync else: 0, 16 /*samples*/)) + +// Make a quick sample scene out of some trees. + +def model_tree(numb, branchl, narrowf, leafs, leafc) -> void: + if numb: + mg.translate(float3_z * branchl): + mg.tapered_cylinder(1.0, narrowf, branchl) + mg.translate(float3_z * branchl): + let axis = sincos(rnd(360)) + let branches = rnd(3) + 1 + for(branches) i: + mg.rotate(float3_z, 360 * i / branches): + mg.rotate(float3(axis, 0.0), 12 * branches): + mg.scale(narrowf): + mg.sphere(1.0) + model_tree(numb - 1, branchl, narrowf, leafs, leafc) + else: + mg.color(leafc): + mg.sphere(leafs) + +mg.smooth(0.5) +mg.color(color { 0.7, 0.6, 0.5, 1.0 }) +model_tree(10, 1.5, 0.75, 12, color { 0.6, 1.0, 0.6, 1.0 }) +let tree = mg.polygonize(50) + +mg.smooth(0.0) +mg.color(color_red) +mg.sphere(1.0) +let sphere = mg.polygonize(3) + +let camera = Camera { float3 { 0.0, 0.0, 3.0 }, 135.0, 20.0 } + +let balls = []::[float3] + +let controller_meshes = map(10): nil + +def drawworld(): + gl.clear(color_grey) + gl.blend(blend_none) + + gl.light(camera.position, float2 { 64.0, 1.0 }) + //gl.light(float3(sincos(gl.time() * 20), 0) * 100 + float3_z * 100) + + gl.set_shader("flat") + for(10) i: + gl.translate sincos(i * 72.0) * 3.0: + gl.scale(0.3): + gl.render_mesh(tree) + + for(balls) b: + gl.translate b[0]: + gl.scale(0.1): + gl.rotate_z(sincos(gl.time() * 30.0)): + gl.render_mesh(sphere) + + gl.set_shader("color") + gl.debug_grid(int3 { 20, 20, 0 }, float3_1, 0.005) + +while gl.frame(): + if gl.button("escape") == 1: return + + let znear = 0.1 + let zfar = 1000 + if vrmode: + vr.start() + for(2) e: + vr.start_eye(e, znear, zfar) + set_z_up_worldspace(): + drawworld() + for(vr.num_motion_controllers()) mc: + if vr.motioncontrollerstracking(mc): + var mcmesh = controller_meshes[mc] + if not mcmesh: + // FIXME: sadly have to do this on the fly because at init controllers and + // meshes are not available for some reason. + controller_meshes[mc] = mcmesh = vr.create_motion_controller_mesh(mc) + if mcmesh: + gl.color(color_white) + gl.push_model_view() + vr.motion_controller(mc) + gl.set_shader("phong_textured") + gl.render_mesh(mcmesh) + gl.pop_model_view() + vr.finish() + for(2) mc: + if vr.motion_controller_button(mc, "trigger") == 1: + let pos = to_z_up(vr.motion_controller_vec(mc, 3)) + let dir = to_z_up(vr.motion_controller_vec(mc, 2)) + balls.push([ pos, -dir ]) + // Render the two eye textures to the non-VR window for feedback: + gl.clear(color_black) + gl.blend(blend_none) + gl.set_shader("textured") + + for(2) e: + vr.set_eye_texture(0, e) + gl.translate(float2 { gl.window_size().x * e / 2.0, gl.window_size().y }): + gl.rect(float2 { gl.window_size().x / 2.0, -gl.window_size().y }) + else: + // This is how we'd render normally, with no HMD connected: + gl.cursor(false) + camera.FPS_update("w", "a", "s", "d", 10.0, 4.0, true) + gl.clear(color_dark_grey) + gl.blend(blend_none) + gl.perspective(70.0, znear, zfar) + camera.FPS_view() + drawworld() + + if balls.length > 100: + balls.remove_range(0, balls.length - 100) + for(balls) b: + b[0] += b[1] * gl.delta_time() / 10.0 diff --git a/samples/Lobster/wave_function_collapse.lobster b/samples/Lobster/wave_function_collapse.lobster new file mode 100644 index 000000000..bcaa88690 --- /dev/null +++ b/samples/Lobster/wave_function_collapse.lobster @@ -0,0 +1,94 @@ +// Example of using Wave Function Collapse to generate gameworlds based on tiles. +// Using ascii chars here for simplicity. + +let tilemap = [ + " /--\\ ", // Not aligned because we escape \\ + " | | ", + " | | ", + "/--J L--\\ ", + "| | ", + "| | ", + "L--\\ /--J ", + " | | ", + " | | ", + " L--J ", + " ", +] + +let benchmark = false + +if benchmark: + var no_conflicts = 0 + for(1000) i: + let _, conflicts = wave_function_collapse(tilemap, int2 { 100, 100 }) + print "{i}: {conflicts}" + if not conflicts: no_conflicts++ + print no_conflicts + print seconds_elapsed() + +else: + // Just print a single no-conflict example. + for(100) i: + let outmap, conflicts = wave_function_collapse(tilemap, int2 { 100, 50 }) + if not conflicts: + print "iteration: {i}" + for(outmap) s: print s + return from program + +/* + +prints: + + | | /-\ /-------J | | L-J /--J + | /--J | | | | | | + | | | L-J | | /-\ | +-----------J /-J | | | /------------\ | | | / + | L-\ /----J | | | | | | | + | | | /--J | | | L----J | + | | L-\ | | | | | +----\ | L-\ | | /-\ /-J | | L + | | | | | | | | L-------------\ | /---\ + | | | /-----J L-J L--J | L-\ | | + | | | | | | | | + | | | | /-------\ | | | | + | | | | | | | L----J | + | | /---------J L-----\ | /---J | | + /-J | | | /-----J | /-J /----J + | | | | /----\ | | | | + | | | /-\ /----J | | | | | | + | | | | | | | | | | | /------J + | | | | | /-J /--\ /-----J | | | | | + | L-J | | | | | | | | | /--J | + L-----\ | | | | L--J | L---\ | | | /--\ /----\ + | /---\ | | | | | | L------J | | | | | + | | | | | | | | | | | | | | +--\ | | L-----J | | | | | | | | | L-- + | | | | L--\ | /--------J | | | | /--J +--J /--J | /-\ | | | | | /--J | | | /----- + | L---\ | | | | | | | | | L---J | + | | | | | | | | | /----\ | | | + | L--J | | | | | /-\ | | | | | /----\ L----\ + | | | L-J | | | | | | | | | | | + L------------\ | | L-------J | | | | | | | | | + | | | | | | | | | | L--\ | + /-------------J | | /---\ | L------J | | | | | | + | /-J | | | /---J | | | | | | + | | L------\ | L---\ | | | | L--\ /--J | + | /-\ L-\ | | | | | L-----\ | | | | + | | | | | | | | /----\ | /-\ | L--\ | | | +--\ | | | | L---J | L--\ | | | | | | | | | L + | | | | | | | | /-J | L-J | | | | + L-J L-J L---------\ | | | | | /--J | | | + | L-\ | | | | | | | L---\ +----\ /---\ | | L------J | | | | | | /-- + | | | | | | | L--\ | | | | + | L---J | /---J | | | L--J | | + | | /--\ | /--\ | | | | | + L------\ /-----J | | | | | | | /--J | | + | /--\ | | | | L--J | | | | | + | | | | | | | /-\ L-------\ | L----------\ | | + | | L---J | | | | | | | | L--J + | | | | | | | L-J | + +*/ + diff --git a/samples/Lobster/will_it_shuffle.lobster b/samples/Lobster/will_it_shuffle.lobster new file mode 100644 index 000000000..a42c2fbf8 --- /dev/null +++ b/samples/Lobster/will_it_shuffle.lobster @@ -0,0 +1,205 @@ +// Adaptation of https://bost.ocks.org/mike/shuffle/compare.html +// Adapted by Joshua T Corbin +// +// TODO: would be nice to measure reshuffle time, in particular, adapt better +// to expensive reshuffles that keep blowing the frame time budget; degrade +// gracefully, and scale back the dataset size and/or round count + +import color +import gl +import imgui +import std +import vec +import gl + +enum shuffle_mode: + shuffle_none = 0 + shuffle_naive_swap + shuffle_push_remove + shuffle_fisher_yates + +let mode = shuffle_none + +def do_shuffle(array): switch mode: + case shuffle_none: array + case shuffle_naive_swap: naive_swap(array) + case shuffle_push_remove: push_remove(array) + case shuffle_fisher_yates: fisher_yates(array) + +def mode_name(): return switch mode: + case shuffle_none: "none" + case shuffle_naive_swap: "naive swap" + case shuffle_push_remove: "push remove" + case shuffle_fisher_yates: "fisher yates" + +def naive_swap(xs): + // ala https://blog.codinghorror.com/the-danger-of-naivete/ + for(xs) x, i: + let j = rnd(xs.length) + xs[i] = xs[j] + xs[j] = x + +def push_remove(xs): + for(xs.length) i: + xs.push(xs.remove(rnd(xs.length - i))) + +def fisher_yates(xs): + for(xs) x, i: + if i: + let j = rnd(i + 1) + xs[i] = xs[j] + xs[j] = x + +let colors = map([ + // from https://github.com/d3/d3-scale-chromatic/blob/master/src/diverging/PuOr.js + 0x2D004B, + 0x542788, + 0x8073AC, + 0xB2ABD2, + 0xD8DAEB, + 0xF7F7F7, + 0xFEE0B6, + 0xFDB863, + 0xE08214, + 0xB35806, + 0x7F3B08, +]): color { + div( (_ >> 16) & 0xFF, 255), + div( (_ >> 8) & 0xFF, 255), + div( _ & 0xFF, 255), + 1.0, +} + +def spline_value(values, t): + // uniforem b-spline interpolation adapted from d3-scale-chromactic + t = clamp(t, 0.0, 1.0) + let n = values.length - 1 + let i = clamp(floor(t * n), 0, n-1) + + let v1 = values[i] + let v2 = values[i + 1] + let v0 = if i > 0: values[i - 1] else: v1 * 2.0 - v2 + let v3 = if i < n - 1: values[i + 2] else: v2 * 2.0 - v1 + + let t1 = (t - div(i, n)) * n + let t2 = t1 * t1 + let t3 = t2 * t1 + return ( + v0 * (1.0 - 3.0 * t1 + 3.0 * t2 - t3) + + v1 * (4.0 - 6.0 * t2 + 3.0 * t3) + + v2 * (1.0 + 3.0 * t1 + 3.0 * t2 - 3.0 * t3) + + v3 * t3 + ) / 6.0 + +def spline(t): + let c = spline_value(colors, t) + return color { + clamp(c.red, 0.0, 1.0), + clamp(c.green, 0.0, 1.0), + clamp(c.blue, 0.0, 1.0), + clamp(c.alpha, 0.0, 1.0), + } + +var N = 60 +var M = 10000 +var matrix = map(N*N): 0 +var fills = map(N*N): color_black +var color_domain_lo = log(M / N / 3) +var color_domain_hi = log(M / N * 3) + +def reshuffle(): + for(N) i: for(N) j: matrix[N*i + j] = 0 + for M: + let array = map(N): _ + do_shuffle(array) + for(array) i, j: + matrix[N*i + j]++ + for(matrix) n, k: + let t0 = color_domain_lo + let t1 = color_domain_hi + let v = clamp(log(n), t0, t1) + let t = if t0 == t1: 0.5 else: clamp((v - t0) / (t1 - t0), 0.0, 1.0) + fills[k] = spline(t) + +let shuffling = true + +fatal(gl.window("Will It Shuffle?", 640, 480)) +check(gl.set_font_name("data/fonts/US101/US101.ttf") and gl.set_font_size(32), "cannot load gl font") + +class shuffle_routine: + last_n = 0 + last_m = 0 + last_mode = mode + + def resume(): + // apply any changes in mode, N, or M + let changed = N != last_n or M != last_m + if changed: + let max_work = 60 * 10000 + if N * M > max_work: + if N != last_n: + M = floor(div(max_work, N) / 100.0) * 100 + else: + N = floor(div(max_work, M)) + if N != last_n: + matrix = map(N*N): 0 + fills = map(N*N): color_black + last_n = N + color_domain_lo = log(M / N / 3) + color_domain_hi = log(M / N * 3) + last_m = M + + if shuffling or changed or last_mode != mode: + reshuffle() + last_mode = mode + +let shuffler = shuffle_routine {} + +def viz(): + let status = "mode:{mode_name()} rounds:{M}" + + let window_size = gl.window_size() + let unit = min(floor((float(window_size) / float2 { N + 1, N + 2 }))) + let size = int2 { N + 1, N + 2 } * unit + gl.set_font_size(unit) + + let status_size = gl.text_size(status) + + for(N) i: + gl.translate(float2 { unit * (1.0 + i), 0.0 }): + gl.text("{i}") + gl.translate(float2 { 0.0, unit * (1.0 + i) }): + gl.text("{i}") + gl.scale(unit): + for(N) i: for(N) j: + gl.translate(float2 { 1 + i, 1 + j }): + gl.color(fills[N*i + j]): + gl.rect(float2 { 0.95, 0.95 }) + gl.translate(float2 { (size.x - status_size.x) / 2, unit * (N + 1) }): + gl.text(status) + +class ui: + def init(): + im.init(true) + let im.fontsize = 32.0 + // check(im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", im.fontsize), "cannot load imgui font") + check(im.add_font("data/fonts/Inconsolata/Inconsolata-Bold.ttf", im.fontsize), "cannot load imgui font") + + def resume(): + im.frame(): + im.window("ctl", im.window_always_autoresize): + im.show_engine_stats() + if not shuffling: + if im.button("Reshuffle"): + reshuffle() + im.show_vars() + +let uier = ui {} +uier.init() + +while gl.frame(): + gl.clear(color_black) + gl.color(color_white) + shuffler.resume() + viz() + uier.resume() diff --git a/vendor/README.md b/vendor/README.md index 9abe3a704..77ce11e7d 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -327,6 +327,7 @@ This is a list of grammars that Linguist selects to provide syntax highlighting - **Literate Haskell:** [atom-haskell/language-haskell](https://github.com/atom-haskell/language-haskell) - **LiveCode Script:** [Ferruslogic/vscode-livecodescript](https://github.com/Ferruslogic/vscode-livecodescript) - **LiveScript:** [paulmillr/LiveScript.tmbundle](https://github.com/paulmillr/LiveScript.tmbundle) +- **Lobster:** [inferrna/lobster_ling](https://github.com/inferrna/lobster_ling) - **Logos:** [Cykey/Sublime-Logos](https://github.com/Cykey/Sublime-Logos) - **Logtalk:** [textmate/logtalk.tmbundle](https://github.com/textmate/logtalk.tmbundle) - **LookML:** [atom/language-yaml](https://github.com/atom/language-yaml) diff --git a/vendor/grammars/lobster_ling b/vendor/grammars/lobster_ling new file mode 160000 index 000000000..9a0b9becc --- /dev/null +++ b/vendor/grammars/lobster_ling @@ -0,0 +1 @@ +Subproject commit 9a0b9becc6966d09e9477ec2e3ef63fec6bb7bbe From 02943e6eaf42c3a54945d202891e16925501efb8 Mon Sep 17 00:00:00 2001 From: inferrna Date: Sat, 18 Jan 2025 22:35:11 +0400 Subject: [PATCH 2/8] Lobster language color --- lib/linguist/languages.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml index 713466140..b7ed136e4 100644 --- a/lib/linguist/languages.yml +++ b/lib/linguist/languages.yml @@ -4031,6 +4031,7 @@ LiveScript: language_id: 208 Lobster: type: programming + color: "#f95428" extensions: - ".lobster" ace_mode: python From a440688de96b5ae0c22dfc17232c9fe0a4a51590 Mon Sep 17 00:00:00 2001 From: inferrna Date: Sun, 19 Jan 2025 23:16:31 +0400 Subject: [PATCH 3/8] Less examples --- samples/Lobster/bspgraphic.lobster | 41 ---- samples/Lobster/collision.lobster | 46 ---- samples/Lobster/cube.lobster | 150 ------------- .../Lobster/custom_shader_metaballs.lobster | 40 ---- samples/Lobster/custom_shader_sobel.lobster | 52 ----- samples/Lobster/dragtest.lobster | 43 ---- samples/Lobster/dungeongen.lobster | 89 -------- samples/Lobster/guitest.lobster | 66 ------ samples/Lobster/imguitest.lobster | 79 ------- samples/Lobster/lazy_py_triples.lobster | 69 ------ samples/Lobster/lobbytest.lobster | 134 ------------ samples/Lobster/mgtest.lobster | 198 ----------------- samples/Lobster/pendulum.lobster | 68 ------ samples/Lobster/physics_water.lobster | 48 ---- samples/Lobster/textinput.lobster | 36 --- samples/Lobster/threads.lobster | 39 ---- samples/Lobster/vonkoch.lobster | 44 ---- samples/Lobster/vrtest.lobster | 126 ----------- .../Lobster/wave_function_collapse.lobster | 94 -------- samples/Lobster/will_it_shuffle.lobster | 205 ------------------ 20 files changed, 1667 deletions(-) delete mode 100644 samples/Lobster/bspgraphic.lobster delete mode 100644 samples/Lobster/collision.lobster delete mode 100644 samples/Lobster/cube.lobster delete mode 100644 samples/Lobster/custom_shader_metaballs.lobster delete mode 100644 samples/Lobster/custom_shader_sobel.lobster delete mode 100644 samples/Lobster/dragtest.lobster delete mode 100644 samples/Lobster/dungeongen.lobster delete mode 100644 samples/Lobster/guitest.lobster delete mode 100644 samples/Lobster/imguitest.lobster delete mode 100644 samples/Lobster/lazy_py_triples.lobster delete mode 100644 samples/Lobster/lobbytest.lobster delete mode 100644 samples/Lobster/mgtest.lobster delete mode 100644 samples/Lobster/pendulum.lobster delete mode 100644 samples/Lobster/physics_water.lobster delete mode 100644 samples/Lobster/textinput.lobster delete mode 100644 samples/Lobster/threads.lobster delete mode 100644 samples/Lobster/vonkoch.lobster delete mode 100644 samples/Lobster/vrtest.lobster delete mode 100644 samples/Lobster/wave_function_collapse.lobster delete mode 100644 samples/Lobster/will_it_shuffle.lobster diff --git a/samples/Lobster/bspgraphic.lobster b/samples/Lobster/bspgraphic.lobster deleted file mode 100644 index 8372b33ee..000000000 --- a/samples/Lobster/bspgraphic.lobster +++ /dev/null @@ -1,41 +0,0 @@ -import vec -import color -import gl - -let dirs = [ int2_x, int2_y ] - -def square(pos, size, depth) -> void: - if (rnd(4) or depth < 2) and size.x > 8 and size.y > 8: - var notfound = true - var dir = 0 - var split = 0 - while notfound: // FIXME - dir = 0 - split = rnd(size.x + size.y) - if split >= size.x: - split -= size.x - dir = 1 - if split >= 3 and split <= size[dir] - 3: - notfound = false - for(2) i: - let width = if i: size[dir] - split else: split - square(pos + dirs[dir] * split * i, dirs[dir] * width + dirs[1 - dir] * size[1 - dir], depth + 1) - else: - gl.translate pos: - gl.color(color_black) - gl.rect(float(size)) - let range = max(0.1, min(0.7, 1.0 - sqrt(size.x * size.y) / 250.0)) - let col = color { rnd_float(), rnd_float() * range + (1.0 - range), rnd_float(), 1.0 } * 0.15 + 0.75 - gl.color(col) - gl.rect(float(size - int2_1 * 2)) - -var seed = 342342432 - -fatal(gl.window("bsp", 512, 512)) - -while gl.frame(): - if gl.button("escape") == 1: return - if gl.button("space") == 1: seed += 675656 - gl.clear(color_black) - rnd_seed(seed) - square(int2_0, gl.window_size(), 0) diff --git a/samples/Lobster/collision.lobster b/samples/Lobster/collision.lobster deleted file mode 100644 index 310e56e6d..000000000 --- a/samples/Lobster/collision.lobster +++ /dev/null @@ -1,46 +0,0 @@ -// Demonstrate the use of "circles_within_range", which is a simple function to implement -// fast collision or other algorithms that need to know about nearby objects. - -import std -import vec -import color -import gl - -let num_balls = 1000 // try 10000 ! -let ball_size = 1.0 / sqrt(num_balls) / 3.0 -let ball_speed = 0.001 -let push_force = 0.2 // Needs to be balanced with speed. - -let positions = map(num_balls): rnd_float2() -let radiuses = map(num_balls): (rnd_float() + 0.5) * ball_size -let dirs = map(num_balls): rnd_float2_norm() - -fatal(gl.window("collision", 1024, 1024)) - -while gl.frame(): - if gl.button("escape") == 1: return - gl.window_title("FPS: {(1.0 / gl.delta_time())}") - gl.clear(color_black) - gl.scale(float(gl.window_size().y)) - - // Move balls out of their own motivation. - for(num_balls) i: - positions[i] += dirs[i] * ball_speed - // Keep within bounds. - if not positions[i].x.in_range(1.0): dirs[i] *= float2 { -1.0, 1.0 } - if not positions[i].y.in_range(1.0): dirs[i] *= float2 { 1.0, -1.0 } - - // For each ball, find all nearby balls in an efficient manner. - let push_indices = circles_within_range(0.0, positions, radiuses, [], [], int2_0) - - // Now use this information to push other balls away. - for(push_indices) iv, j: - for(iv) i: - let v = positions[j] - positions[i] - let dist = magnitude(v) - radiuses[i] - radiuses[j] - positions[i] += -normalize(v) * push_force * -dist - - // Render them. - for(positions) p, i: - gl.translate p: - gl.circle(radiuses[i], 20) diff --git a/samples/Lobster/cube.lobster b/samples/Lobster/cube.lobster deleted file mode 100644 index 8292c8012..000000000 --- a/samples/Lobster/cube.lobster +++ /dev/null @@ -1,150 +0,0 @@ -// This example renders a cube with a different colorèd number on each face. -// Moving the mouse or WASD rotates the viewport around the cube like turning a -// globe in front of the camera. -// -// The example illustrates creating a simple mesh and rendering a texture on -// the mesh in various orientations. -// The example showcases the render_to_texture utility, which allows us to -// render a 2D drawing on the surfaces of a 3D object, using a frame buffer. -// The example also demostrates using gl.new_mesh, which allows us to orient -// the texture on each face of the cube, since the simpler gl.new_poly is not -// quite sufficient for this purpose. -// -// To generate the mesh for each face, cube_face_meshes uses the three least -// significant bits of ASCII characters to represent whether the edge is on the -// near or far side of the cube along each axis. -// -// -----ZYX CHR corner -// 00110000 '0' origin -// 00110001 '1' x -// 00110010 '2' y -// 00110011 '3' xy -// 00110100 '4' z -// 00110101 '5' xz -// 00110110 '6' yz -// 00110111 '7' zyz -// -// Each face of the cube contains four of the cube's vertices. -// The normal vector for the face of the cube must face outward to be opaque to -// an outside observer, so the vertices are listed counter-clockwise. -// In the following illustration, the interior faces are inverted, so the -// vertices appear in clockwise order from our perspective. -// -// The first index must be the top-right corner of the texture. -// The textures are arranged such that the textures are upright around the -// equator and the poles are connected top to bottom with their nearest -// neighbor. -// Rotating the vertex strings rotates the corresponding texture orientation. -// -// The faces are numbered according to the conventions of right-handed dice. -// All faces in opposition have the same sum. -// Numbers read counter-clockwise about the origin and its opposite vertex. -// -// inverted clockwise -// Z 4---5 .---. .---. -// \ |\ |\ 1540->2 |\ 2 \ | |\ 6<-5464 -// 0--X | 0---1 | .---. | 6 | . -// | | | | | 0462->3 |3| | | |4| 4<-5137 -// Y 6-|-7 | ' | 1 | '---' | -// \| \| 3102->1 \| | \ 5 \| 5<-7326 -// 2---3 '---' '---' -// counter-clockwise -// -// Kris Kowal - -import vec -import color -import gl -import texture -import camera - -fatal(gl.window("Lobster Cube", 515, 515)) -check(gl.set_font_name("data/fonts/Droid_Sans/DroidSans.ttf"), "can\'t load font") - -// cube_face_meshes contains the meshes for the six faces of a cube, -// facing outward. -let cube_face_vertices = [ - "3102", // 1 - "1540", // 2 - "0462", // 3 - "5137", // 4 - "7326", // 5 - "5764", // 6 -] - -// These are in order from top-right, counter-clockwise. -// I can offer no explanation why the origin is not the top- or bottom-left. -let square = [float2_0, float2_x, float2_1, float2_y] - -let cube_face_meshes = map(cube_face_vertices) v: - let positions = map(v) c: vec3_v(map(3) i: float(c & (1 << (2 - i)) != 0)) - let indices = [0, 1, 2, 2, 3, 0] - gl.new_mesh( - "PT", - positions, // "P" - [], // colors, - [], // normals, - square, // "T" texcoords, - [], // textcoords2, - indices - ) - -// Use the frame buffer to render a unique texture for each face of the cube, -// with its number. -// We use white on grey since we can use these as color multipliers where we -// render the mesh. -let detail = 256 -let cube_face_textures = map(6) i: - render_to_texture(nil, int2_1 * detail, false, nil, texture_format_nomipmap): - gl.ortho(true, false) - let label = "{i+1}" - gl.set_font_size(detail/2) - let size = gl.text_size(label) - gl.translate(float2_1 * float(detail) / 2.0 - float(size) / 2.0) - gl.clear(color_grey) - gl.color(color_white) - gl.text(label) - -// Colors are arranged such that CMY are about the origin and RGB on the polar -// opposites. -// Colors on opposite faces are also opposite hues. -let face_colors = [ - color_purple, // M - color_olive, // Y - color_teal, // C - color_dark_red, // R - color_dark_blue, // G - color_dark_green, // B -] - -// Rotate the camera to place the origin vertex of the cube in the center of -// the view. -let camera = Camera { float3_0, -45.0, 45.0 } - -// This demo is able to use camera.FPS_view but uses a different control model -// to move the camera about the origin at a fixed “elevation”. -def camera_GPS_update(upkey, leftkey, downkey, rightkey, elevation:float, mousesens:float, keyspeed:float): - let long = (gl.button(upkey) >= 1) - (gl.button(downkey) >= 1) - let lat = (gl.button(rightkey) >= 1) - (gl.button(leftkey) >= 1) - camera.pitch -= gl.mouse_delta(0).y / mousesens + long * keyspeed - camera.yaw -= gl.mouse_delta(0).x / mousesens - lat * keyspeed - camera.pitch = min(85.0, max(-85.0, camera.pitch)) - camera.position = vecfromyawpitch(camera.yaw, camera.pitch, -elevation, 0.0) - -while gl.frame() and not gl.button("escape"): - gl.clear(color_dark_grey) - gl.cursor(false) - gl.perspective(60.0, 0.1, 1000.0) - - camera_GPS_update("w", "a", "s", "d", 2.0, 4.0, 4.0) - camera.FPS_view() - - gl.translate(float3_1 / -2.0) - gl.set_shader("textured") - for(6) i: - // The texture colors are multiplied by the color in context. - // Since the texture on our mesh is white on black, we can change the - // white to a unique color for each face of the world. - gl.color(face_colors[i]) - gl.set_primitive_texture(0, cube_face_textures[i]) - gl.render_mesh(cube_face_meshes[i]) diff --git a/samples/Lobster/custom_shader_metaballs.lobster b/samples/Lobster/custom_shader_metaballs.lobster deleted file mode 100644 index af2eb6986..000000000 --- a/samples/Lobster/custom_shader_metaballs.lobster +++ /dev/null @@ -1,40 +0,0 @@ -import vec -import color -import gl -import texture - -// Example of how to do run a custom shader on a full window rectangle. -// In this case, a simple metaballs example adapted from -// https://www.shadertoy.com/view/XssSzN - -fatal(gl.window("custom shader", 1024, 1024)) - -let mats = -""" -SHADER metaballs - VERTEX - INPUTS apos:4 atc:2 - UNIFORMS mvp - gl_Position = mvp * apos; - itc = atc; - PIXEL - INPUTS itc:2 - UNIFORM float time - vec2 uv = itc * 2.0 - 1.0; - float v = 0.0; - for (int i = 0; i < 100; i++) { - vec2 c = sin(time * (0.1 + float(i) / 300.0) + vec2(i, -i)); - v += 1.0 - smoothstep(0.0, 0.2, length(uv - c)); - } - frag_color = vec4(mix(vec3(v), vec3(1.0), smoothstep(0.9, 0.9, v)), 1.0); -""" - -fatal(gl.load_materials(mats, true)) - -while(gl.frame()): - if gl.button("escape") == 1: return - gl.clear(color_black) - gl.blend(blend_none) - gl.set_shader("metaballs") - gl.set_uniform("time", gl.time()) - gl.rect(float(gl.window_size())) diff --git a/samples/Lobster/custom_shader_sobel.lobster b/samples/Lobster/custom_shader_sobel.lobster deleted file mode 100644 index 2f4365abc..000000000 --- a/samples/Lobster/custom_shader_sobel.lobster +++ /dev/null @@ -1,52 +0,0 @@ -import vec -import color -import gl -import texture - -// Example of how to do run a custom shader as a post-process on a scene. -// In this case, a simple sobel edge detection filter example adapted from -// https://www.shadertoy.com/view/4t3XDM - -fatal(gl.window("custom shader", 1024, 1024)) - -let mats = -""" -SHADER sobel - VERTEX - INPUTS apos:4 atc:2 - UNIFORMS mvp - gl_Position = mvp * apos; - itc = atc; - PIXEL - INPUTS itc:2 - UNIFORMS tex0 framebuffer_size - vec3 TL = texture(tex0, itc + vec2(-1, 1)/ framebuffer_size).rgb; - vec3 TM = texture(tex0, itc + vec2( 0, 1)/ framebuffer_size).rgb; - vec3 TR = texture(tex0, itc + vec2( 1, 1)/ framebuffer_size).rgb; - vec3 ML = texture(tex0, itc + vec2(-1, 0)/ framebuffer_size).rgb; - vec3 MR = texture(tex0, itc + vec2( 1, 0)/ framebuffer_size).rgb; - vec3 BL = texture(tex0, itc + vec2(-1, -1)/ framebuffer_size).rgb; - vec3 BM = texture(tex0, itc + vec2( 0, -1)/ framebuffer_size).rgb; - vec3 BR = texture(tex0, itc + vec2( 1, -1)/ framebuffer_size).rgb; - vec3 grad_x = -TL + TR - 2.0 * ML + 2.0 * MR - BL + BR; - vec3 grad_y = TL + 2.0 * TM + TR - BL - 2.0 * BM - BR; - frag_color.r = length(vec2(grad_x.r, grad_y.r)); - frag_color.g = length(vec2(grad_x.g, grad_y.g)); - frag_color.b = length(vec2(grad_x.b, grad_y.b)); -""" - -fatal(gl.load_materials(mats, true)) - -var tex = nil - -while(gl.frame()): - if gl.button("escape") == 1: return - // Render the body to a texture, then the post-process of it to - // the screen. - tex = post_process(tex, gl.window_size(), "sobel", false, - texture_format_nomipmap): - rnd_seed(0) - gl.clear(color_black) - for(256) i: - gl.translate rnd_float2() * (sincos(gl.time() * 10.0 + i) * 0.5 + 0.5) * float(gl.window_size()): - gl.circle(50.0, 7) diff --git a/samples/Lobster/dragtest.lobster b/samples/Lobster/dragtest.lobster deleted file mode 100644 index 0b05edc72..000000000 --- a/samples/Lobster/dragtest.lobster +++ /dev/null @@ -1,43 +0,0 @@ -// demonstrates how Lobster's event state can help program common operations without having to store state yourself - -import vec -import color -import gl - -fatal(gl.window("event animation and mouse drag test", 1024, 1024)) - -let lines = [] - -while gl.frame(): - - if gl.button("escape") == 1: return - - gl.clear(color_black) - gl.color(color_white) - - // allow user to drag a line, and store it when complete - // note how we can track a drag operation conveniently thanks to gl.last_pos - let start = float(gl.last_pos("mouse1", true)) - let cur = float(gl.mouse_pos(0)) - if magnitude(start - cur) > 1.0: // a click is not a drag - let down, up = gl.button("mouse1") - if down >= 1: // show feedback - gl.line(start, cur, 1.0) - if up == 1: // store line - lines.push([start, float(gl.last_pos("mouse1", false))]) - // note how we use the mouse up position, which may be != cur - - // draw previous lines - gl.color(color_light_grey) - for(lines) l: - gl.line(l[0], l[1], 1.0) - - // just for demonstration purposes, gl.last_time makes it easy to animate clicks etc. - // we show little animated circles on each mouse down or up - for(2) down: - gl.color(if down: color_green else: color_blue) - let clicktime = gl.time() - gl.last_time("mouse1", down) - if clicktime < 1.0: - gl.translate gl.last_pos("mouse1", down): - gl.line_mode 1: - gl.circle(clicktime * 20.0, 10) diff --git a/samples/Lobster/dungeongen.lobster b/samples/Lobster/dungeongen.lobster deleted file mode 100644 index 77f29dedd..000000000 --- a/samples/Lobster/dungeongen.lobster +++ /dev/null @@ -1,89 +0,0 @@ -// based on: https://gist.github.com/munificent/b1bcd969063da3e6c298be070a22b604 -// via: https://gist.github.com/Joker-vD/cc5372a349559b9d1a3b220d5eaf2b01 - -import std -import vec -import color -import gl - -let TILE_VOID = ' ' -let TILE_FLOOR = '.' -let TILE_WALL = '#' -let TILE_CORNER = '!' -let TILE_OPEN_DOOR = '\'' -let TILE_CLOSED_DOOR = '+' -let TILE_PLAYER = '@' - -let fsize = int2 { 80, 40 } -let field = mapxy(fsize): TILE_VOID - -def cave(with_player): - // csize/start are all inner dimensions/coordinates (w/o walls) - let csize = rnd_int2(int2 { 10, 6 }) + int2 { 5, 3 } - let start = rnd_int2(fsize - csize - 2) + 1 - - // Cave iterator function that supplies wall/corner type. - def in_box(f): - forxy(csize + 2) v: - v += start - 1 - let at_wall = (v < start) + (v >= start + csize) - // We need to somehow record corners of all caves to check - // for intersections later, so we use a special tile for it - f(v, [ TILE_FLOOR, TILE_WALL, TILE_CORNER ][manhattan(at_wall)]) - - // Check if the new cave (with walls) intersects with the interior of - // any already existing cave. Touching walls/corners are okay - in_box() v: - if field[v] == TILE_FLOOR: return - - // Find a suitable place for a door - var door_counter = 0 - var door_pos = int2_0 - if not with_player: - in_box() v, tile: - // The door should not be created in the cave's corner or over - // another door, or in another cave's corner. It's impossible - // to make a cave without a door, because rnd always - // returns 0. - if tile == TILE_WALL and field[v] == TILE_WALL: - door_counter++ - if rnd(door_counter) == 0: door_pos = v - - // If the cave's walls were made completely out of corners - // and doors, don't make such a cave - if door_counter == 0: return - - // The cave looks okay, let's draw it. First, draw the walls and the floor - in_box() v, tile: - field[v] = tile - - // Now draw the door. - if door_counter > 0: - field[door_pos] = if rnd(2): TILE_OPEN_DOOR else: TILE_CLOSED_DOOR - - if with_player: - // A cave with the player has only the player inside it - field[rnd_int2(csize) + start] = TILE_PLAYER - else: - // A cave without the player has some random mobs and/or gold in it; - // 1d6 of entities total, 25% chance of gold, 75% of a mob. - // Mob letters range from 'A' to '~', inclusive - for rnd(6) + 1: - field[rnd_int2(csize) + start] = - if rnd(4) == 0: '$' else: 'A' + rnd('~' - 'A' + 1) - -rnd_seed(int(seconds_elapsed() * 1000000.0)) - -// A call to cave() is not guaranteed to actually make a new cave, -// so call it many times -for(1000) j: - cave(j == 0) - -// Remove special corner type. -forxy(fsize) v: - if field[v] == TILE_CORNER: - field[v] = TILE_WALL - -// Print the generated field -for(field) row: - print unicode_to_string(row) diff --git a/samples/Lobster/guitest.lobster b/samples/Lobster/guitest.lobster deleted file mode 100644 index 65c918164..000000000 --- a/samples/Lobster/guitest.lobster +++ /dev/null @@ -1,66 +0,0 @@ -import gui - -fatal(gl.window("gui test", 960, 640)) -check(gl.set_font_name("data/fonts/US101/US101.ttf"), "can\'t load font!") -//check(gl.set_font_name("C:\\Windows\\Fonts\\arialuni.ttf"), "can\'t load font!") - -let ghosttex = gl.load_texture("data/textures/lobster.jpg") -assert ghosttex - -var clicked = false -var focused = false - -while gl.frame(): - if gl.button("escape") == 1: - return - - let fontsize = gl.window_size().y / 10 - gl.set_font_size(fontsize, fontsize / 40.0) - - gl.clear(color_grey) - gl.color(color_white) - - gui.setclickcolors(color_dark_green, color_green) - - gui.start(gl.window_size(), 1, 1, true): - gui.hblock(1, 0): - //gui.fontscale(0.3): - // gui.text(unicode_to_string(map(500): _)) - - gui.text("A") - gui.fontscale(2): - gui.text("B") - gui.vblock(2, 0): - gui.text("1") - gui.hblock(1, 0.1): - gui.text("2") - gui.text("2") - gui.text("2") - gui.divider(2) - gui.text("3") - gui.text("4") - gui.space(0.2) - gui.divider(4) - gui.space(0.2) - gui.text("C") - gui.button(): - gui.border(int2_1 * 2): - gui.fontscale(2): - gui.text("clickme") - fn: - clicked = not clicked - fn: - focused = not focused - if clicked: - gui.color(color_red): - //gui.text(unicode_to_string([0x30E6, 0x30FC, 0x30B6, 0x30FC, 0x5225, 0x30B5, 0x30A4, 0x30C8])) - gui.text("ON") - gui.text(focused and "C" or "E") - gui.background(color_blue, false): - gui.margin(int2_1 * 4): - gui.text("D") - if(clicked): - gui.blend(2): - gui.image(float2_1, ghosttex) - gui.text("E") - gui.text("E") \ No newline at end of file diff --git a/samples/Lobster/imguitest.lobster b/samples/Lobster/imguitest.lobster deleted file mode 100644 index af7fd6488..000000000 --- a/samples/Lobster/imguitest.lobster +++ /dev/null @@ -1,79 +0,0 @@ -// simple demo showing integration of https://github.com/ocornut/imgui - -import vec -import color -import gl -import imgui -import gl - -fatal(gl.window("imgui demo", 1280, 1024)) - -im.init(false, im.config_docking_enable, 3.0) -assert im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", 20.0) - -// The data we're editing: -class Thingy: - on = true - i = 1 - ratio = 0.5 - name = "Fred" - col = color_red - pos = float2_1 - fruits = ["apple", "banana", "pear"] - others:[Thingy] - windowflag = im.window_always_autoresize - -var d = Thingy { map(3): Thingy { [] } } -var demo = false -let foo = "bar" // Will show up in debug view. - -while gl.frame(): - gl.clear(color_grey) - - // <- Render game here. - - im.frame(): - // Maximum convenience: turn any data structure into editable UI! - im.window("Automatically created from a class", d.windowflag): - d = im.edit_anything(d) - - // Manually created widgets, so you can choose how to represent and layout data: - im.window("Created by code", im.window_no_collapse | im.window_always_autoresize): - im.tab_bar("tab_bar"): - im.tab("Widgets"): - im.text("A text!") - d.on = im.checkbox("Check it out!", d.on) - im.tooltip("This is worth checking out, really!") - d.ratio = im.sliderfloat("Float", d.ratio, 0.0, 1.0) - d.i = im.sliderint("Int", d.i, 0, 10) - d.name = im.input_text("Name", d.name) - d.i = im.radio(d.fruits, d.i, true) - d.i = im.combo("Combo", d.fruits, d.i) - d.i = im.listbox("ListBox", d.fruits, d.i, d.fruits.length) - d.col = im.coloredit("Color", d.col) - for(2) g: im.graph("Sin", map(20): sin(_ * 20.0) + 1.0, g) - if im.button("popup!"): - im.popup_open("pop1") - im.popup("pop1"): - im.selectable("select a") - im.selectable("select b") - im.tab("Debug"): - if not demo and im.button("show demo window"): demo = true - if im.button("Cause breakpoint() hit"): - // The debugger should have popped up! - // The program is frozen until you hit continue. - // Try putting these breakpoints anywhere in your own code. - breakpoint() - - // Show the built-in imgui demo window, for testing. - if demo: demo = im.window_demo() - - // Realtime debugging! Show all lobster variables as editable UI. - // Also show some engine stats, like e.g. a framerate graph. - // These are shown in realtime, if you want to pause and see local - // variables in functions, try breakpoint() above - im.window("Realtime Debugger", im.window_always_autoresize): - im.show_engine_stats() - im.treenode("Memory Usage"): - im.text(get_memory_usage(20)) - im.show_vars() diff --git a/samples/Lobster/lazy_py_triples.lobster b/samples/Lobster/lazy_py_triples.lobster deleted file mode 100644 index ae7ac6db9..000000000 --- a/samples/Lobster/lazy_py_triples.lobster +++ /dev/null @@ -1,69 +0,0 @@ -/* Inpired by: https://aras-p.info/blog/2018/12/28/Modern-C-Lamentations/ - -You can't do "lazy evaluation" in most traditional languages, because you either can't abstract -over the end result (lack of lambdas, like in C), or you can't terminate the lazyness (can't -return from lambda, like in C++ and most other languages). - -C++ works around this by having chained iterable objects (ranges). -Haskell has lazy lists, but these require a "de-forestation" optimization to be efficient. - -And Lobster can do all of this easily and efficiently because.. you can actually return -out of lambdas to enclosing functions! -*/ - -// A generator that yields values indefinitely (well, it assumes you'll use "take" below -// before you run out of integer bits). -def ints(start, f): - while true: f(start++) - -// Version with an actual end. -def ints(start, end, f): - for(end - start + 1) i: f(start + i) - -// Generic way to only consume n values out of a lazy generator. -def take(n, gen): - var i = 0 - gen() f: - // This returns from take (and thus terminates all generators in between), which is not - // possible in most languages. - if i++ == n: return - f() - - -// Now we can do generic lazy "Pythagorean Triples", Lobster style: -def py_triples(n, f): - take(n) yield: - ints(1) z: - ints(1, z) x: - ints(x, z) y: - if x * x + y * y == z * z: - yield(): - f([ x, y, z ]) - -// Note that the language guarantees that lambdas get inlined (and so do named single use / small -// functions), so the above code mostly compiles to a bunch of nested loops, so this code should -// actually be efficient! - -// Also note this code is self contained, and fully statically typed. - -// Print the first 100. -py_triples(100) t: - print t - -/* Outputs: - -[3, 4, 5] -[6, 8, 10] -[5, 12, 13] -[9, 12, 15] -[8, 15, 17] -[12, 16, 20] -[7, 24, 25] -[15, 20, 25] -[10, 24, 26] -... -[65, 156, 169] -[119, 120, 169] -[26, 168, 170] - -*/ diff --git a/samples/Lobster/lobbytest.lobster b/samples/Lobster/lobbytest.lobster deleted file mode 100644 index 74987d107..000000000 --- a/samples/Lobster/lobbytest.lobster +++ /dev/null @@ -1,134 +0,0 @@ -import color -import imgui -import gl - -fatal(gl.window("lobby demo", 1280, 1024)) - -im.init(false, im.config_docking_enable, 3.0) -assert im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", 20.0) - -let LOBBY_MAX_MEMBERS = 4 - -let steamerr = steam.init(0, true, true) -if steamerr == 0: - print "Steam init failed" -elif steamerr < 0: - // Not started from Steam, exit this instance, Steam will launch again. - // This only happens if there wasn't a steam.appid.txt file. - print "Relaunching from Steam..." - return -else: - print "Welcome Steam user {steam.username()}" - -var new_key = "" -var new_value = "" - -def lobby_tree(label, lobby_ids, in_lobby): - im.treenode(label): - for(lobby_ids) id, idx: - let num_members = steam.lobby_get_num_members(id) - im.treenode("#{idx}: {id} ({num_members} members)"): - if in_lobby: - if im.button("Leave##{id}"): - steam.lobby_leave(id) - im.treenode("Modify:"): - new_key = im.input_text("Key", new_key) - new_value = im.input_text("Value", new_value) - if im.button("Add Data"): - steam.lobby_set_data(id, new_key, new_value) - new_key = "" - new_value = "" - im.treenode("Server:"): - let server_id = steam.lobby_get_game_server(id) - im.text("{server_id}") - else: - if im.button("Join##{id}"): - steam.lobby_join(id) - im.treenode("Data:"): - let keys, values = steam.lobby_get_all_data(id) - assert keys.length == values.length - for(keys.length) i: - if in_lobby: - if im.button("-##{id}_{i}"): - steam.lobby_delete_data(id, keys[i]) - im.same_line() - im.text("\"{keys[i]}\": \"{values[i]}\"") - if in_lobby: - let members = steam.lobby_get_members(id) - im.treenode("Members (#{members.length}):"): - for(members) m, i: - im.text("#{i}: {steam.friend_get_username(m)} ({m})") - -enum FilterType: - FT_Numerical - FT_String - FT_ResultCount -let filter_type_names = ["Numeric", "String", "Result Count"] - -enum CompareType: - CT_LTE = -2 - CT_LT = -1 - CT_EQ = 0 - CT_GT = 1 - CT_GTE = 2 - CT_NE = 3 -let compare_type_names = ["<=", "<", "==", ">", ">=", "!="] - -class Filter: - type: FilterType - key: string - int_value = 0 - string_value = "" - cmp = CT_EQ - -let filters = []::Filter - -while gl.frame(): - gl.clear(color_grey) - - im.frame(): - im.window("Lobbies", 0): - // Create Lobby - if im.button("Create Lobby"): - steam.lobby_create(LOBBY_MAX_MEMBERS) - let joined = steam.lobby_get_all_joined() - lobby_tree("Joined:", joined, true) - // Filters - if im.button("Add Filter"): - filters.push(Filter { FT_Numerical, "" }) - im.treenode("Filters:"): - var to_remove = -1 - for(filters) f, i: - im.treenode("#{i}"): - if im.button("Delete"): - to_remove = i - f.type = FilterType(im.combo("Type", filter_type_names, f.type)) - switch f.type: - case FT_Numerical: - f.key = im.input_text("Key", f.key) - f.int_value = im.input_int("Value", f.int_value, -10000, 10000) - f.cmp = CompareType(im.combo("Compare", compare_type_names, f.cmp - CT_LTE) + CT_LTE) - case FT_String: - f.key = im.input_text("Key", f.key) - f.string_value = im.input_text("Value", f.string_value) - f.cmp = CompareType(im.combo("Compare", compare_type_names, f.cmp - CT_LTE) + CT_LTE) - case FT_ResultCount: - f.int_value = im.input_int("Count", f.int_value, 1, 50) - if to_remove >= 0: - filters.remove(to_remove) - // Request Lobbies - if im.button("Request Lobbies"): - for(filters) f: - switch f.type: - case FT_Numerical: - steam.lobby_request_add_numerical_filter(f.key, f.int_value, f.cmp) - case FT_String: - steam.lobby_request_add_string_filter(f.key, f.string_value, f.cmp) - case FT_ResultCount: - steam.lobby_request_add_result_count_filter(f.int_value) - steam.lobby_request_list() - let matched = steam.lobby_request_get_lobbies() - if im.button("Refresh lobby data"): - for(matched) id: - steam.lobby_request_data(id) - lobby_tree("Matched:", matched, false) diff --git a/samples/Lobster/mgtest.lobster b/samples/Lobster/mgtest.lobster deleted file mode 100644 index 358d1693f..000000000 --- a/samples/Lobster/mgtest.lobster +++ /dev/null @@ -1,198 +0,0 @@ -// graphics demo showing Lobster's builtin facilities of generating meshes procedurally from implicit functions -// this functionality is under construction - -import vec -import color -import gl -import camera -import gl -import mg - -print "generating models... this may take some time!" - -fatal(gl.window("meshgen test (PLEASE WAIT...)", 1280, 800, gl.window_init_linear_color)) - -let camera = Camera { float3_1 * 10.0, 135.0, 0.0 } - -def mg.mirror_y(dist, body): - for(2) i: - mg.translate(float3 { 0.0, i * dist * 2.0 - dist, 0.0 }, body) - -//mg.set_color_noise(0.5, 1) -let aspoints = false -mg.set_point_mode(aspoints) -if not aspoints: - //mg.set_polygon_reduction(3, 0.98, 0.95) - mg.set_vertex_randomize(0.0) - -let density = 80 - -var rounded_cube = nil -if true: - mg.smooth(1.0) - mg.color(color_white) - mg.rotate(float3_1, 45): - mg.superquadric(float3_1 * 10.0, float3_1) - rounded_cube = mg.polygonize(density) - -var gun = nil -if true: - mg.smooth(0.5) - mg.color(color_white) - mg.rotate(float3_y, 90.0): - mg.scale_vec(float3 { 1.0, 1.3, 1.0 }): - mg.supertoroid(3.0, float3 { 3.0, 3.0, 5.0 }) - mg.translate(float3 { 0.0, -1.3, 2.0 }): - mg.rotate(float3_x, 20.0): - mg.superquadric(float3 { 3.0, 3.0, 3.0 }, float3 { 0.5, 0.5, 3.5 }) - mg.translate(float3 { 0.0, -8.0, 4.0 }): - mg.rotate(float3_x, 30.0): - mg.superquadric(float3 { 3.0, 3.0, 3.0 }, float3 { 0.5, 0.5, 2.5 }) - mg.translate(float3 { 0.0, -5.5, -3.0 }): - mg.rotate(float3_x, -20.0): - mg.superquadric(float3 { 5.0, 5.0, 100.0 }, float3 { 1.5, 2.5, 6.5 }) - mg.translate(float3 { 0.0, 2.0, 4.2 }): - mg.superquadric(float3 { 3.0, 100.0, 3.0 }, float3 { 2.0, 10.0, 3.0 }) - mg.translate(float3 { 0.0, 14.0, 5.0 }): - mg.rotate(float3_x, 90.0): - mg.cylinder(1.0, 3.0) - gun = mg.polygonize(density) - -var spaceship = nil -if true: - mg.smooth(0.5) - mg.color(color_white) - mg.superquadric_non_uniform(float3 { 1.0, 2.0, 2.0 }, float3 { 2.0, 2.0, 2.0 }, - float3 { 2.0, 3.5, 0.3 }, float3 { 0.5, 3.5, 0.3 }) - mg.superquadric_non_uniform(float3 { 2.0, 1.5, 1.0 }, float3 { 2.0, 1.5, 2.0 }, - float3 { 4.0, 1.0, 0.7 }, float3 { 0.7, 1.0, 0.4 }) - mg.translate(float3 { 1.0, 0.0, 0.4 }): - mg.superquadric_non_uniform(float3 { 2.0, 2.0, 1.0 }, float3 { 2.0, 2.0, 2.0 }, - float3 { 1.0, 0.5, 0.4 }, float3 { 0.5, 0.5, 0.5 }) - mg.mirror_y(1.5): - mg.translate(float3_x): - mg.superquadric(float3 { 100.0, 2.0, 2.0 }, float3 { 1.3, 0.2, 0.2 }) - mg.mirror_y(2): - mg.translate(float3_x): - mg.superquadric(float3 { 100.0, 2.0, 2.0 }, float3 { 1.0, 0.15, 0.15 }) - mg.mirror_y(1): - mg.translate(float3_x * -0.6): - mg.superquadric_non_uniform(float3 { 1.0, 2.0, 2.0 }, float3 { 1.0, 2.0, 2.0 }, - float3 { 1.5, 0.5, 0.5 }, float3 { 0.01, 0.5, 0.5 }) - mg.color(color { 1.0, 1.0, 1.0, 0.0 /* carve */ }): - mg.rotate(float3_y, 90.0): - mg.smooth(0.0): - mg.cylinder(0.35, 0.25) - spaceship = mg.polygonize(density * 2) - -def model_tree(numb, branchl, narrowf, leafs, leafc) -> void: - if numb: - mg.translate(float3_z * branchl): - mg.tapered_cylinder(1.0, narrowf, branchl) - mg.translate(float3_z * branchl): - let axis = float3(sincos(rnd(360)), 0.0) - let branches = rnd(3) + 1 - for(branches) i: - mg.rotate(float3_z, 360 * i / branches): - mg.rotate(axis, 12 * branches): - mg.scale(narrowf): - mg.sphere(1.0) - model_tree(numb - 1, branchl, narrowf, leafs, leafc) - else: - mg.color(leafc): - mg.sphere(leafs) - -let trees = [] - -if true: - mg.smooth(0.5) - mg.color(color { 0.6, 0.5, 0.4, 1.0 }) - model_tree(10, 1.5, 0.75, 12, color { 0.4, 1.0, 0.4, 1.0 }) - trees.push(mg.polygonize(density)) - - mg.smooth(0.5) - mg.color(color { 0.4, 0.3, 0.2, 1.0 }) - model_tree(10, 1.2, 0.8, 1.2, color { 0.6, 1.0, 0.3, 1.0 }) - trees.push(mg.polygonize(density)) - - mg.smooth(0.5) - mg.color(color { 0.5, 0.4, 0.3, 1.0 }) - model_tree(9, 2.0, 0.7, 15, color { 0.4, 0.6, 0.4, 1.0 }) - trees.push(mg.polygonize(density)) - -var landscape = nil -// Currently expensive way to do a landscape. -if true: - mg.smooth(0.1) - mg.color(color { 0.6, 0.5, 0.4, 1.0 }) - mg.landscape(0.5, 1.0) - landscape = mg.polygonize(density) - -if gun: print "vertcount: gun {gl.mesh_size(gun)}" -if spaceship: print "vertcount: ship {gl.mesh_size(spaceship)}" -for trees: print("vertcount: tree1 {gl.mesh_size(_)}") -if rounded_cube: print "vertcount: rounded_cube {gl.mesh_size(rounded_cube)}" - -print seconds_elapsed() - -while gl.frame(): - if gl.button("escape") == 1: return - - gl.window_title("meshgen test: {gl.delta_time()}") - - gl.clear(color_darkest_grey) - - let fovscale = 1.5 - - if true: - gl.cursor(false) - gl.perspective(60.0, 0.1, 1000.0) - - var speed = 10 - if gl.button("left shift") >= 1: speed = 50 - camera.FPS_update("w", "a", "s", "d", speed, 4.0, true) - camera.FPS_view() - gl.light(camera.position, float2 { 128.0, 0.1 }) - else: - gl.ortho3d(float3_0, float3 { gl.window_size().x, gl.window_size().y, 2000 } / 100) - gl.rotate_x(sincos(45)) - gl.rotate_z(sincos(45)) - gl.light(float3(sincos(gl.time() * 10), 0) * 100 + float3_z * 300, float2 { 128.0, 0.1 }) - - gl.blend 0: - gl.set_shader(aspoints and "phong_particle" or "flat") - - if spaceship: - gl.translate(float2_1 * 10.0): - gl.point_scale(1.0 * fovscale) - gl.render_mesh(spaceship) - - if gun: - gl.translate(float2_1 * 20.0): - gl.scale(0.2): - gl.point_scale(0.2 * fovscale) - gl.render_mesh(gun) - - if landscape: - gl.translate(float2_y * 30.0): - gl.scale(10): - gl.point_scale(10.0 * fovscale) - gl.render_mesh(landscape) - - if rounded_cube: - gl.translate(float2_y * 10.0): - gl.scale(2): - gl.point_scale(3.0 * fovscale) - gl.render_mesh(rounded_cube) - - for(10) j: - gl.translate(float3_y * j * -5): - for(trees) t, i: - gl.translate(float3_x * i * 10.0): - gl.scale(0.5): - gl.point_scale(0.5 * fovscale) - gl.render_mesh(t) - - gl.set_shader("color") - gl.debug_grid(int3 { 20, 20, 0 }, float3_1, 0.005) - diff --git a/samples/Lobster/pendulum.lobster b/samples/Lobster/pendulum.lobster deleted file mode 100644 index 9c5ed00f2..000000000 --- a/samples/Lobster/pendulum.lobster +++ /dev/null @@ -1,68 +0,0 @@ -// based on: http://www.physicsandbox.com/projects/double-pendulum.html - -import std -import vec -import color -import gl -import texture - -let g = 9.8 -let mass = 1 -let max_steps = 2000 - -class link: - len:float - dtheta = 0.0 - theta:float - - def ddl(): return dtheta * dtheta * len - -class pendulum: - links:[link] - pts = []::float2 - col:color - - def update(time): - let sc = sincos(links[0].theta - links[1].theta) - for(links) l, i: - let o = links[1 - i] - let d2theta = ((i + 1) * g * (sin(o.theta) * sc.x - (2.0 - i) * sin(l.theta)) + - (i * 2.0 - 1.0) * ((i + 1) * ddl(o) + ddl(l) * sc.x) * sc.y) / - (l.len * (2.0 - sc.x * sc.x)) - l.dtheta += d2theta * time - l.theta += degrees(l.dtheta * time) - - def render(): - gl.blend(blend_add): - for(pts) pos, i: - gl.color(col * i / max_steps) - gl.translate(pos): - gl.circle(10.0, 10) - - gl.color(color_white) - var pos = float2_0 - for(links) l: - let p = yx(sincos(l.theta)) * l.len - gl.line(float2_0, p, 2.0) - gl.translate(p) - gl.circle(4.0, 10) - pos += p - pts.push(pos) - if pts.length > max_steps: pts.remove(0) - -let pendula = map(5) i: - pendulum { - map(2): link { 100.0 + i * 25.0, 181.0 + i * 0.1 }, - color { 0.20 - i * 0.04, 0.075, i * 0.04, 1.0 } - } - -fatal(gl.window("double pendulum", 700, 700)) -while gl.frame(): - if gl.button("escape") == 1: return - gl.clear(color_black) - let time = gl.delta_time() * 4.0 - for(pendula) p: - gl.ortho() - gl.translate(float(gl.window_size()) / 2.0) - p.update(time) - p.render() diff --git a/samples/Lobster/physics_water.lobster b/samples/Lobster/physics_water.lobster deleted file mode 100644 index 4428dc84c..000000000 --- a/samples/Lobster/physics_water.lobster +++ /dev/null @@ -1,48 +0,0 @@ -// Showing off physics features in Lobster - -import vec -import color -import gl -import texture -import camera -import physics -import gl - -fatal(gl.window("Physics demo : water", 1024, 768, gl.window_init_linear_color)) - -let worldsize = float2 { 60.0, 40.0 } - -ph.initialize(float2 { 0.0, -10.0 }) -ph.initialize_particles(0.15) -ph.set_shader(nil, "color_attr_particle") - -let floor = ph.create_box(float2 { 0.0, 1.0 }, float2 { 20.0, 1.0 }) -let wall1 = ph.create_box(float2 { -20.0, 7.0 }, float2 { 1.0, 7.0 }) -let wall2 = ph.create_box(float2 { 20.0, 7.0 }, float2 { 1.0, 7.0 }) - -floor.ph.set_color(color_dark_grey) -wall1.ph.set_color(color_dark_grey) -wall2.ph.set_color(color_dark_grey) - -let boxes = map 5: - let b = ph.create_box(float2 { 0.0, 10.0 + _ * 3.0 }, float2 { 1.0, 1.0 }) - b.ph.dynamic(true) - b - -ph.create_particle_circle(float2 { 0.0, 5.0 }, 7.0, color_red, ph.colormixingparticle) - -while gl.frame() and gl.button("escape") != 1: - gl.clear(color_black) - - // create right-handed coordinate system, with (0, 0) at the bottom middle - set_2d_worldspace(worldsize, float2 { 0.5, 1.0 }, float2 { 1.0, -1.0 }) - - if gl.button("mouse1") >= 1: - ph.create_particle_circle(gl.local_mouse_pos(0), 0.5, color_blue, ph.colormixingparticle) - - ph.step(gl.delta_time(), 8, 3) - - gl.blend(blend_add_alpha) - ph.render_particles(2.5) - gl.blend(blend_none) - ph.render() diff --git a/samples/Lobster/textinput.lobster b/samples/Lobster/textinput.lobster deleted file mode 100644 index 596658743..000000000 --- a/samples/Lobster/textinput.lobster +++ /dev/null @@ -1,36 +0,0 @@ -// Shows how you could input text strings. - -import std -import vec -import color -import gl - -gl.window("text input", 1200, 600) -check(gl.set_font_name("data/fonts/Inconsolata/Inconsolata-Regular.ttf"), "font!") - -gl.start_text_input(int2_0, int2_1 * gl.get_font_size()) - -let log = [] - -while gl.frame(): - gl.clear(color_black) - gl.set_font_size(30) - // This returns other values to render IME correctly, which we'll skip here. - let text = gl.text_input_state() - gl.text(text + "_") - if gl.button("return") == 1: - // Here we normally end the input. - log.insert(0, text) - gl.end_text_input() - // For now, go right back to inputting a new string: - gl.start_text_input(int2_0, int2_1 * gl.get_font_size()) - elif gl.key_repeat("backspace") == 1: - // Inefficient but simple way to drop last character. - let uv = string_to_unicode(text) - if uv.length: - uv.pop() - gl.set_text_input(unicode_to_string(uv)) - // Render all strings input so far: - for(log) l, i: - gl.translate(float2 { 0.0, gl.get_font_size() * (i + 2.0) }): - gl.text(l) diff --git a/samples/Lobster/threads.lobster b/samples/Lobster/threads.lobster deleted file mode 100644 index a3ac24218..000000000 --- a/samples/Lobster/threads.lobster +++ /dev/null @@ -1,39 +0,0 @@ -// Simple multi-threading example. - -class fib_request: - attribute serializable - n:int - -class fib_response: - attribute serializable - fr:int - -def fib(n:int) -> int: - return if n <= 1: n else: fib(n - 1) + fib(n - 2) - -def time(name, f): - let start = seconds_elapsed() - f() - print "{name} took: {(seconds_elapsed() - start)}" - -if not is_worker_thread(): - let hwthreads, hwcores = thread_information() - print "running on {hwthreads} hardware threads and {hwcores} cores" - let fibn = 30 - let fibr = 832040 - time("single thread fib({fibn})"): assert fib(fibn) == fibr - // Let's launch some threads! - start_worker_threads(hwthreads) - let num_fibs = hwthreads - time("parallel fib({fibn}) * {num_fibs}"): - // Now Q up some work: - for num_fibs: thread_write(fib_request { fibn }) - // And wait for the responses. - for num_fibs: - let r = thread_read(typeof fib_response) - assert r and r.fr == fibr -else: - // We're a worker.. process jobs. - while workers_alive(): - let r = thread_read(typeof fib_request) - if r: thread_write(fib_response { fib(r.n) }) diff --git a/samples/Lobster/vonkoch.lobster b/samples/Lobster/vonkoch.lobster deleted file mode 100644 index 2e41d8a5b..000000000 --- a/samples/Lobster/vonkoch.lobster +++ /dev/null @@ -1,44 +0,0 @@ -import color -import gl -import imgui -import vec - -// Sequence of the angles for the Von Koch curve's segment. Each angle -// represent a rotation to be done after the previous segment is drawn. -let seq = [ -60, 120, -60, 0 ] - -def curve(depth, d) -> void: - if depth <= 0: - // Draw a single segment and move to its end. - gl.line(float2 { 0.0, 0.0 }, float2 { d, 0.0 }, 0.002) - gl.translate(float2 { d, 0.0 }) - else: - for (seq) angle: - curve(depth - 1, d / 3.0) - gl.rotate_z(sincos(angle)) - -fatal(gl.window("Von Koch\'s curve", 640, 640)) -im.init(false) -assert im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", 20.0) - -class Setup: - depth: int - -var setup = Setup { 5 } - -while gl.frame(): - if gl.button("escape") == 1: return - gl.clear(color_white) - gl.color(color_blue) - gl.scale(gl.window_size().y * 0.75) - gl.translate(float2 { 0.1, 0.4 }) - - // Draw the Von koch's curve segments around a tringle - for (3): - curve(setup.depth, 1.0) - gl.rotate_z(sincos(120.0)) - - im.frame(): - im.window("Parameters", im.window_always_autoresize): - setup = im.edit_anything(setup) - diff --git a/samples/Lobster/vrtest.lobster b/samples/Lobster/vrtest.lobster deleted file mode 100644 index dfa018bc9..000000000 --- a/samples/Lobster/vrtest.lobster +++ /dev/null @@ -1,126 +0,0 @@ -// graphics demo showing Lobster's built-in loading of (animated) .iqm models - -import vec -import color -import gl -import texture -import camera -import gl -import mg - -let vrmode = vr.init() - -fatal(gl.window(if vrmode: "VR Test" else: "No VR device", 1280, 640, - if vrmode: gl.window_init_no_vsync else: 0, 16 /*samples*/)) - -// Make a quick sample scene out of some trees. - -def model_tree(numb, branchl, narrowf, leafs, leafc) -> void: - if numb: - mg.translate(float3_z * branchl): - mg.tapered_cylinder(1.0, narrowf, branchl) - mg.translate(float3_z * branchl): - let axis = sincos(rnd(360)) - let branches = rnd(3) + 1 - for(branches) i: - mg.rotate(float3_z, 360 * i / branches): - mg.rotate(float3(axis, 0.0), 12 * branches): - mg.scale(narrowf): - mg.sphere(1.0) - model_tree(numb - 1, branchl, narrowf, leafs, leafc) - else: - mg.color(leafc): - mg.sphere(leafs) - -mg.smooth(0.5) -mg.color(color { 0.7, 0.6, 0.5, 1.0 }) -model_tree(10, 1.5, 0.75, 12, color { 0.6, 1.0, 0.6, 1.0 }) -let tree = mg.polygonize(50) - -mg.smooth(0.0) -mg.color(color_red) -mg.sphere(1.0) -let sphere = mg.polygonize(3) - -let camera = Camera { float3 { 0.0, 0.0, 3.0 }, 135.0, 20.0 } - -let balls = []::[float3] - -let controller_meshes = map(10): nil - -def drawworld(): - gl.clear(color_grey) - gl.blend(blend_none) - - gl.light(camera.position, float2 { 64.0, 1.0 }) - //gl.light(float3(sincos(gl.time() * 20), 0) * 100 + float3_z * 100) - - gl.set_shader("flat") - for(10) i: - gl.translate sincos(i * 72.0) * 3.0: - gl.scale(0.3): - gl.render_mesh(tree) - - for(balls) b: - gl.translate b[0]: - gl.scale(0.1): - gl.rotate_z(sincos(gl.time() * 30.0)): - gl.render_mesh(sphere) - - gl.set_shader("color") - gl.debug_grid(int3 { 20, 20, 0 }, float3_1, 0.005) - -while gl.frame(): - if gl.button("escape") == 1: return - - let znear = 0.1 - let zfar = 1000 - if vrmode: - vr.start() - for(2) e: - vr.start_eye(e, znear, zfar) - set_z_up_worldspace(): - drawworld() - for(vr.num_motion_controllers()) mc: - if vr.motioncontrollerstracking(mc): - var mcmesh = controller_meshes[mc] - if not mcmesh: - // FIXME: sadly have to do this on the fly because at init controllers and - // meshes are not available for some reason. - controller_meshes[mc] = mcmesh = vr.create_motion_controller_mesh(mc) - if mcmesh: - gl.color(color_white) - gl.push_model_view() - vr.motion_controller(mc) - gl.set_shader("phong_textured") - gl.render_mesh(mcmesh) - gl.pop_model_view() - vr.finish() - for(2) mc: - if vr.motion_controller_button(mc, "trigger") == 1: - let pos = to_z_up(vr.motion_controller_vec(mc, 3)) - let dir = to_z_up(vr.motion_controller_vec(mc, 2)) - balls.push([ pos, -dir ]) - // Render the two eye textures to the non-VR window for feedback: - gl.clear(color_black) - gl.blend(blend_none) - gl.set_shader("textured") - - for(2) e: - vr.set_eye_texture(0, e) - gl.translate(float2 { gl.window_size().x * e / 2.0, gl.window_size().y }): - gl.rect(float2 { gl.window_size().x / 2.0, -gl.window_size().y }) - else: - // This is how we'd render normally, with no HMD connected: - gl.cursor(false) - camera.FPS_update("w", "a", "s", "d", 10.0, 4.0, true) - gl.clear(color_dark_grey) - gl.blend(blend_none) - gl.perspective(70.0, znear, zfar) - camera.FPS_view() - drawworld() - - if balls.length > 100: - balls.remove_range(0, balls.length - 100) - for(balls) b: - b[0] += b[1] * gl.delta_time() / 10.0 diff --git a/samples/Lobster/wave_function_collapse.lobster b/samples/Lobster/wave_function_collapse.lobster deleted file mode 100644 index bcaa88690..000000000 --- a/samples/Lobster/wave_function_collapse.lobster +++ /dev/null @@ -1,94 +0,0 @@ -// Example of using Wave Function Collapse to generate gameworlds based on tiles. -// Using ascii chars here for simplicity. - -let tilemap = [ - " /--\\ ", // Not aligned because we escape \\ - " | | ", - " | | ", - "/--J L--\\ ", - "| | ", - "| | ", - "L--\\ /--J ", - " | | ", - " | | ", - " L--J ", - " ", -] - -let benchmark = false - -if benchmark: - var no_conflicts = 0 - for(1000) i: - let _, conflicts = wave_function_collapse(tilemap, int2 { 100, 100 }) - print "{i}: {conflicts}" - if not conflicts: no_conflicts++ - print no_conflicts - print seconds_elapsed() - -else: - // Just print a single no-conflict example. - for(100) i: - let outmap, conflicts = wave_function_collapse(tilemap, int2 { 100, 50 }) - if not conflicts: - print "iteration: {i}" - for(outmap) s: print s - return from program - -/* - -prints: - - | | /-\ /-------J | | L-J /--J - | /--J | | | | | | - | | | L-J | | /-\ | ------------J /-J | | | /------------\ | | | / - | L-\ /----J | | | | | | | - | | | /--J | | | L----J | - | | L-\ | | | | | -----\ | L-\ | | /-\ /-J | | L - | | | | | | | | L-------------\ | /---\ - | | | /-----J L-J L--J | L-\ | | - | | | | | | | | - | | | | /-------\ | | | | - | | | | | | | L----J | - | | /---------J L-----\ | /---J | | - /-J | | | /-----J | /-J /----J - | | | | /----\ | | | | - | | | /-\ /----J | | | | | | - | | | | | | | | | | | /------J - | | | | | /-J /--\ /-----J | | | | | - | L-J | | | | | | | | | /--J | - L-----\ | | | | L--J | L---\ | | | /--\ /----\ - | /---\ | | | | | | L------J | | | | | - | | | | | | | | | | | | | | ---\ | | L-----J | | | | | | | | | L-- - | | | | L--\ | /--------J | | | | /--J ---J /--J | /-\ | | | | | /--J | | | /----- - | L---\ | | | | | | | | | L---J | - | | | | | | | | | /----\ | | | - | L--J | | | | | /-\ | | | | | /----\ L----\ - | | | L-J | | | | | | | | | | | - L------------\ | | L-------J | | | | | | | | | - | | | | | | | | | | L--\ | - /-------------J | | /---\ | L------J | | | | | | - | /-J | | | /---J | | | | | | - | | L------\ | L---\ | | | | L--\ /--J | - | /-\ L-\ | | | | | L-----\ | | | | - | | | | | | | | /----\ | /-\ | L--\ | | | ---\ | | | | L---J | L--\ | | | | | | | | | L - | | | | | | | | /-J | L-J | | | | - L-J L-J L---------\ | | | | | /--J | | | - | L-\ | | | | | | | L---\ -----\ /---\ | | L------J | | | | | | /-- - | | | | | | | L--\ | | | | - | L---J | /---J | | | L--J | | - | | /--\ | /--\ | | | | | - L------\ /-----J | | | | | | | /--J | | - | /--\ | | | | L--J | | | | | - | | | | | | | /-\ L-------\ | L----------\ | | - | | L---J | | | | | | | | L--J - | | | | | | | L-J | - -*/ - diff --git a/samples/Lobster/will_it_shuffle.lobster b/samples/Lobster/will_it_shuffle.lobster deleted file mode 100644 index a42c2fbf8..000000000 --- a/samples/Lobster/will_it_shuffle.lobster +++ /dev/null @@ -1,205 +0,0 @@ -// Adaptation of https://bost.ocks.org/mike/shuffle/compare.html -// Adapted by Joshua T Corbin -// -// TODO: would be nice to measure reshuffle time, in particular, adapt better -// to expensive reshuffles that keep blowing the frame time budget; degrade -// gracefully, and scale back the dataset size and/or round count - -import color -import gl -import imgui -import std -import vec -import gl - -enum shuffle_mode: - shuffle_none = 0 - shuffle_naive_swap - shuffle_push_remove - shuffle_fisher_yates - -let mode = shuffle_none - -def do_shuffle(array): switch mode: - case shuffle_none: array - case shuffle_naive_swap: naive_swap(array) - case shuffle_push_remove: push_remove(array) - case shuffle_fisher_yates: fisher_yates(array) - -def mode_name(): return switch mode: - case shuffle_none: "none" - case shuffle_naive_swap: "naive swap" - case shuffle_push_remove: "push remove" - case shuffle_fisher_yates: "fisher yates" - -def naive_swap(xs): - // ala https://blog.codinghorror.com/the-danger-of-naivete/ - for(xs) x, i: - let j = rnd(xs.length) - xs[i] = xs[j] - xs[j] = x - -def push_remove(xs): - for(xs.length) i: - xs.push(xs.remove(rnd(xs.length - i))) - -def fisher_yates(xs): - for(xs) x, i: - if i: - let j = rnd(i + 1) - xs[i] = xs[j] - xs[j] = x - -let colors = map([ - // from https://github.com/d3/d3-scale-chromatic/blob/master/src/diverging/PuOr.js - 0x2D004B, - 0x542788, - 0x8073AC, - 0xB2ABD2, - 0xD8DAEB, - 0xF7F7F7, - 0xFEE0B6, - 0xFDB863, - 0xE08214, - 0xB35806, - 0x7F3B08, -]): color { - div( (_ >> 16) & 0xFF, 255), - div( (_ >> 8) & 0xFF, 255), - div( _ & 0xFF, 255), - 1.0, -} - -def spline_value(values, t): - // uniforem b-spline interpolation adapted from d3-scale-chromactic - t = clamp(t, 0.0, 1.0) - let n = values.length - 1 - let i = clamp(floor(t * n), 0, n-1) - - let v1 = values[i] - let v2 = values[i + 1] - let v0 = if i > 0: values[i - 1] else: v1 * 2.0 - v2 - let v3 = if i < n - 1: values[i + 2] else: v2 * 2.0 - v1 - - let t1 = (t - div(i, n)) * n - let t2 = t1 * t1 - let t3 = t2 * t1 - return ( - v0 * (1.0 - 3.0 * t1 + 3.0 * t2 - t3) + - v1 * (4.0 - 6.0 * t2 + 3.0 * t3) + - v2 * (1.0 + 3.0 * t1 + 3.0 * t2 - 3.0 * t3) + - v3 * t3 - ) / 6.0 - -def spline(t): - let c = spline_value(colors, t) - return color { - clamp(c.red, 0.0, 1.0), - clamp(c.green, 0.0, 1.0), - clamp(c.blue, 0.0, 1.0), - clamp(c.alpha, 0.0, 1.0), - } - -var N = 60 -var M = 10000 -var matrix = map(N*N): 0 -var fills = map(N*N): color_black -var color_domain_lo = log(M / N / 3) -var color_domain_hi = log(M / N * 3) - -def reshuffle(): - for(N) i: for(N) j: matrix[N*i + j] = 0 - for M: - let array = map(N): _ - do_shuffle(array) - for(array) i, j: - matrix[N*i + j]++ - for(matrix) n, k: - let t0 = color_domain_lo - let t1 = color_domain_hi - let v = clamp(log(n), t0, t1) - let t = if t0 == t1: 0.5 else: clamp((v - t0) / (t1 - t0), 0.0, 1.0) - fills[k] = spline(t) - -let shuffling = true - -fatal(gl.window("Will It Shuffle?", 640, 480)) -check(gl.set_font_name("data/fonts/US101/US101.ttf") and gl.set_font_size(32), "cannot load gl font") - -class shuffle_routine: - last_n = 0 - last_m = 0 - last_mode = mode - - def resume(): - // apply any changes in mode, N, or M - let changed = N != last_n or M != last_m - if changed: - let max_work = 60 * 10000 - if N * M > max_work: - if N != last_n: - M = floor(div(max_work, N) / 100.0) * 100 - else: - N = floor(div(max_work, M)) - if N != last_n: - matrix = map(N*N): 0 - fills = map(N*N): color_black - last_n = N - color_domain_lo = log(M / N / 3) - color_domain_hi = log(M / N * 3) - last_m = M - - if shuffling or changed or last_mode != mode: - reshuffle() - last_mode = mode - -let shuffler = shuffle_routine {} - -def viz(): - let status = "mode:{mode_name()} rounds:{M}" - - let window_size = gl.window_size() - let unit = min(floor((float(window_size) / float2 { N + 1, N + 2 }))) - let size = int2 { N + 1, N + 2 } * unit - gl.set_font_size(unit) - - let status_size = gl.text_size(status) - - for(N) i: - gl.translate(float2 { unit * (1.0 + i), 0.0 }): - gl.text("{i}") - gl.translate(float2 { 0.0, unit * (1.0 + i) }): - gl.text("{i}") - gl.scale(unit): - for(N) i: for(N) j: - gl.translate(float2 { 1 + i, 1 + j }): - gl.color(fills[N*i + j]): - gl.rect(float2 { 0.95, 0.95 }) - gl.translate(float2 { (size.x - status_size.x) / 2, unit * (N + 1) }): - gl.text(status) - -class ui: - def init(): - im.init(true) - let im.fontsize = 32.0 - // check(im.add_font("data/fonts/Droid_Sans/DroidSans.ttf", im.fontsize), "cannot load imgui font") - check(im.add_font("data/fonts/Inconsolata/Inconsolata-Bold.ttf", im.fontsize), "cannot load imgui font") - - def resume(): - im.frame(): - im.window("ctl", im.window_always_autoresize): - im.show_engine_stats() - if not shuffling: - if im.button("Reshuffle"): - reshuffle() - im.show_vars() - -let uier = ui {} -uier.init() - -while gl.frame(): - gl.clear(color_black) - gl.color(color_white) - shuffler.resume() - viz() - uier.resume() From 415ac4652e1814008d3876483f8967a8ba04bb09 Mon Sep 17 00:00:00 2001 From: inferrna Date: Mon, 20 Jan 2025 13:24:53 +0400 Subject: [PATCH 4/8] Updated lobster_ling repo --- vendor/grammars/lobster_ling | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/grammars/lobster_ling b/vendor/grammars/lobster_ling index 9a0b9becc..8579c6223 160000 --- a/vendor/grammars/lobster_ling +++ b/vendor/grammars/lobster_ling @@ -1 +1 @@ -Subproject commit 9a0b9becc6966d09e9477ec2e3ef63fec6bb7bbe +Subproject commit 8579c622374c36e3cfd4f6764ffad8445d66a61d From db9ca3f9e0f447170819c3281f878c8fdec24c56 Mon Sep 17 00:00:00 2001 From: inferrna Date: Mon, 20 Jan 2025 13:25:29 +0400 Subject: [PATCH 5/8] lobster_ling license --- .../git_submodule/lobster_ling.dep.yml | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 vendor/licenses/git_submodule/lobster_ling.dep.yml diff --git a/vendor/licenses/git_submodule/lobster_ling.dep.yml b/vendor/licenses/git_submodule/lobster_ling.dep.yml new file mode 100644 index 000000000..9bb50e325 --- /dev/null +++ b/vendor/licenses/git_submodule/lobster_ling.dep.yml @@ -0,0 +1,212 @@ +--- +name: lobster_ling +version: 8579c622374c36e3cfd4f6764ffad8445d66a61d +type: git_submodule +homepage: https://github.com/inferrna/lobster_ling.git +license: apache-2.0 +licenses: +- sources: LICENSE.txt + text: |2 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +notices: [] From 04dff29ecf11d7fc820419e3a635c25dc1babe69 Mon Sep 17 00:00:00 2001 From: inferrna Date: Tue, 21 Jan 2025 08:42:40 +0400 Subject: [PATCH 6/8] Fixed path --- vendor/licenses/git_submodule/lobster_ling.dep.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/licenses/git_submodule/lobster_ling.dep.yml b/vendor/licenses/git_submodule/lobster_ling.dep.yml index 9bb50e325..1c1ce82fe 100644 --- a/vendor/licenses/git_submodule/lobster_ling.dep.yml +++ b/vendor/licenses/git_submodule/lobster_ling.dep.yml @@ -1,5 +1,5 @@ --- -name: lobster_ling +name: lobster version: 8579c622374c36e3cfd4f6764ffad8445d66a61d type: git_submodule homepage: https://github.com/inferrna/lobster_ling.git From 8fb097a45fff8f5f4b13fc07f2bc8bd641425043 Mon Sep 17 00:00:00 2001 From: inferrna Date: Tue, 21 Jan 2025 08:52:31 +0400 Subject: [PATCH 7/8] Renamed module --- vendor/grammars/{lobster_ling => lobster} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename vendor/grammars/{lobster_ling => lobster} (100%) diff --git a/vendor/grammars/lobster_ling b/vendor/grammars/lobster similarity index 100% rename from vendor/grammars/lobster_ling rename to vendor/grammars/lobster From 9aa164264f56523d1c26e542f876353a5733ba03 Mon Sep 17 00:00:00 2001 From: inferrna Date: Tue, 21 Jan 2025 11:52:07 +0400 Subject: [PATCH 8/8] Rename module back --- .gitmodules | 4 ++-- grammars.yml | 2 +- vendor/grammars/{lobster => lobster_ling} | 0 vendor/licenses/git_submodule/lobster_ling.dep.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename vendor/grammars/{lobster => lobster_ling} (100%) diff --git a/.gitmodules b/.gitmodules index 5edc56f40..18e9f0856 100644 --- a/.gitmodules +++ b/.gitmodules @@ -875,8 +875,8 @@ [submodule "vendor/grammars/llvm.tmbundle"] path = vendor/grammars/llvm.tmbundle url = https://github.com/whitequark/llvm.tmbundle -[submodule "vendor/grammars/lobster"] - path = vendor/grammars/lobster +[submodule "vendor/grammars/lobster_ling"] + path = vendor/grammars/lobster_ling url = https://github.com/inferrna/lobster_ling.git [submodule "vendor/grammars/logos"] path = vendor/grammars/logos diff --git a/grammars.yml b/grammars.yml index 02c7aa2ec..f0e9db42a 100644 --- a/grammars.yml +++ b/grammars.yml @@ -832,7 +832,7 @@ vendor/grammars/lisp.tmbundle: - source.lisp vendor/grammars/llvm.tmbundle: - source.llvm -vendor/grammars/lobster: +vendor/grammars/lobster_ling: - source.lobster vendor/grammars/logos: - source.logos diff --git a/vendor/grammars/lobster b/vendor/grammars/lobster_ling similarity index 100% rename from vendor/grammars/lobster rename to vendor/grammars/lobster_ling diff --git a/vendor/licenses/git_submodule/lobster_ling.dep.yml b/vendor/licenses/git_submodule/lobster_ling.dep.yml index 1c1ce82fe..9bb50e325 100644 --- a/vendor/licenses/git_submodule/lobster_ling.dep.yml +++ b/vendor/licenses/git_submodule/lobster_ling.dep.yml @@ -1,5 +1,5 @@ --- -name: lobster +name: lobster_ling version: 8579c622374c36e3cfd4f6764ffad8445d66a61d type: git_submodule homepage: https://github.com/inferrna/lobster_ling.git