Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lobster language support #7204

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions grammars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions lib/linguist/languages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4029,6 +4029,14 @@ LiveScript:
codemirror_mode: livescript
codemirror_mime_type: text/x-livescript
language_id: 208
Lobster:
type: programming
color: "#f95428"
extensions:
- ".lobster"
ace_mode: python
tm_scope: source.lobster
language_id: 790066842
Logos:
type: programming
extensions:
Expand Down
41 changes: 41 additions & 0 deletions samples/Lobster/bspgraphic.lobster
Original file line number Diff line number Diff line change
@@ -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)
46 changes: 46 additions & 0 deletions samples/Lobster/collision.lobster
Original file line number Diff line number Diff line change
@@ -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)
150 changes: 150 additions & 0 deletions samples/Lobster/cube.lobster
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>

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])
40 changes: 40 additions & 0 deletions samples/Lobster/custom_shader_metaballs.lobster
Original file line number Diff line number Diff line change
@@ -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()))
52 changes: 52 additions & 0 deletions samples/Lobster/custom_shader_sobel.lobster
Original file line number Diff line number Diff line change
@@ -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)
Loading