Introduce sprite variables

This commit is contained in:
Tony Garnock-Jones 2023-02-04 00:05:06 +01:00
parent 55e4dabea9
commit 6d311f04d6
9 changed files with 529 additions and 231 deletions

View File

@ -19,25 +19,27 @@
(define (bouncy-box)
(define me (symbol->string (strong-gensym 'user)))
(define start-time (current-inexact-milliseconds))
(define-field deadline start-time)
(define-field x (* (- (random) 0.5) 100))
(define x (* (- (random) 0.5) 100))
(define y0 (+ 1.0 (* (random) 10)))
(define-field y y0)
(define-field z (* (- (random) 0.5) 100))
(at ds
(on (asserted (LaterThan (deadline)))
(deadline (+ (deadline) (/ 1000 10)))
(y (+ y0 (cos (/ (- (deadline) start-time) 1000.0 (/ 1 2 pi)))))))
(define z (* (- (random) 0.5) 100))
(define r (random))
(define g (random))
(define b (random))
(define-field y y0)
(define start-time (current-inexact-milliseconds))
(define-field deadline start-time)
(at ds
(on (asserted (LaterThan (deadline)))
(deadline (+ (deadline) (/ 1000 30)))
(y (+ y0 (cos (/ (- (deadline) start-time) 1000.0 (/ 1 2 pi)))))))
(at scene
(assert (Sprite me (Move (Vector3 (x) (y) (z))
(Color-opaque r g b (Box)))))
))
(assert (Variable me 'y (y)))
(assert (Sprite me '(y) (Move (ImmediateVector3 x 'y z) (Color-opaque r g b (Box)))))))
(for [(i 100)] (bouncy-box)))))
(run-tcp-client-relay ds #:hostname "vr.demo.leastfixedpoint.com" #:port 9001
(run-tcp-client-relay ds #:hostname "localhost" #;"vr.demo.leastfixedpoint.com" #:port 9001
#:import (lambda (v) (on-connected (embedded-value v))))))

View File

@ -59,7 +59,10 @@
((and dest
(record
'gravity
(list (app shapes:parse-Vector3 (and ?direction (not (== eof)))))))
(list
(app
shapes:parse-LiteralVector3
(and ?direction (not (== eof)))))))
(Gravity ?direction))
(_ eof)))
(define parse-Gravity! (parse-success-or-error 'parse-Gravity parse-Gravity))
@ -89,7 +92,9 @@
(list
(and ?name (? string?))
(app parse-PortalDestination (and ?destination (not (== eof))))
(app shapes:parse-Vector3 (and ?position (not (== eof)))))))
(app
shapes:parse-LiteralVector3
(and ?position (not (== eof)))))))
(Portal ?name ?destination ?position))
(_ eof)))
(define parse-Portal! (parse-success-or-error 'parse-Portal parse-Portal))

View File

@ -172,21 +172,21 @@
(record
'scale
(list
(app parse-Vector3 (and ?v (not (== eof))))
(app parse-LiteralVector3 (and ?v (not (== eof))))
(app parse-CSGExpr (and ?shape (not (== eof)))))))
(CSGExpr-scale ?v ?shape))
((and dest
(record
'move
(list
(app parse-Vector3 (and ?v (not (== eof))))
(app parse-LiteralVector3 (and ?v (not (== eof))))
(app parse-CSGExpr (and ?shape (not (== eof)))))))
(CSGExpr-move ?v ?shape))
((and dest
(record
'rotate
(list
(app parse-Vector3 (and ?v (not (== eof))))
(app parse-LiteralVector3 (and ?v (not (== eof))))
(app parse-CSGExpr (and ?shape (not (== eof)))))))
(CSGExpr-rotate ?v ?shape))
((and dest
@ -235,9 +235,9 @@
(record
'color
(list
(exact->inexact (*->preserve ?r))
(exact->inexact (*->preserve ?g))
(exact->inexact (*->preserve ?b))
(*->preserve ?r)
(*->preserve ?g)
(*->preserve ?b)
(*->preserve ?shape))))))))
(struct
Color-transparent
@ -253,10 +253,10 @@
(record
'color
(list
(exact->inexact (*->preserve ?r))
(exact->inexact (*->preserve ?g))
(exact->inexact (*->preserve ?b))
(exact->inexact (*->preserve ?alpha))
(*->preserve ?r)
(*->preserve ?g)
(*->preserve ?b)
(*->preserve ?alpha)
(*->preserve ?shape))))))))
(define (parse-Color input)
(match
@ -265,23 +265,53 @@
(record
'color
(list
(and ?r (? flonum?))
(and ?g (? flonum?))
(and ?b (? flonum?))
(app parse-DoubleValue (and ?r (not (== eof))))
(app parse-DoubleValue (and ?g (not (== eof))))
(app parse-DoubleValue (and ?b (not (== eof))))
(app parse-Shape (and ?shape (not (== eof)))))))
(Color-opaque ?r ?g ?b ?shape))
((and dest
(record
'color
(list
(and ?r (? flonum?))
(and ?g (? flonum?))
(and ?b (? flonum?))
(and ?alpha (? flonum?))
(app parse-DoubleValue (and ?r (not (== eof))))
(app parse-DoubleValue (and ?g (not (== eof))))
(app parse-DoubleValue (and ?b (not (== eof))))
(app parse-DoubleValue (and ?alpha (not (== eof))))
(app parse-Shape (and ?shape (not (== eof)))))))
(Color-transparent ?r ?g ?b ?alpha ?shape))
(_ eof)))
(define parse-Color! (parse-success-or-error 'parse-Color parse-Color))
(define (DoubleValue? p)
(or (DoubleValue-immediate? p) (DoubleValue-reference? p)))
(struct
DoubleValue-immediate
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match
preservable
((DoubleValue-immediate src) (exact->inexact (*->preserve src)))))))
(struct
DoubleValue-reference
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match preservable ((DoubleValue-reference src) (*->preserve src))))))
(define (parse-DoubleValue input)
(match
input
((and dest (? flonum?)) (DoubleValue-immediate dest))
((and dest (? symbol?)) (DoubleValue-reference dest))
(_ eof)))
(define parse-DoubleValue!
(parse-success-or-error 'parse-DoubleValue parse-DoubleValue))
(struct
External
(path)
@ -324,7 +354,19 @@
(define parse-Floor! (parse-success-or-error 'parse-Floor parse-Floor))
(struct
Ground
(size)
()
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match preservable ((Ground) (record 'ground (list)))))))
(define (parse-Ground input)
(match input ((and dest (record 'ground (list))) (Ground)) (_ eof)))
(define parse-Ground! (parse-success-or-error 'parse-Ground parse-Ground))
(struct
ImmediateQuaternion
(a b c d)
#:transparent
#:methods
gen:preservable
@ -332,17 +374,84 @@
(define (->preserve preservable)
(match
preservable
((Ground ?size) (record 'ground (list (*->preserve ?size))))))))
(define (parse-Ground input)
((ImmediateQuaternion ?a ?b ?c ?d)
(record
'q
(list
(*->preserve ?a)
(*->preserve ?b)
(*->preserve ?c)
(*->preserve ?d))))))))
(define (parse-ImmediateQuaternion input)
(match
input
((and dest
(record
'ground
(list (app parse-Vector2 (and ?size (not (== eof)))))))
(Ground ?size))
'q
(list
(app parse-DoubleValue (and ?a (not (== eof))))
(app parse-DoubleValue (and ?b (not (== eof))))
(app parse-DoubleValue (and ?c (not (== eof))))
(app parse-DoubleValue (and ?d (not (== eof)))))))
(ImmediateQuaternion ?a ?b ?c ?d))
(_ eof)))
(define parse-Ground! (parse-success-or-error 'parse-Ground parse-Ground))
(define parse-ImmediateQuaternion!
(parse-success-or-error
'parse-ImmediateQuaternion
parse-ImmediateQuaternion))
(struct
ImmediateVector2
(x y)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match
preservable
((ImmediateVector2 ?x ?y)
(record 'v (list (*->preserve ?x) (*->preserve ?y))))))))
(define (parse-ImmediateVector2 input)
(match
input
((and dest
(record
'v
(list
(app parse-DoubleValue (and ?x (not (== eof))))
(app parse-DoubleValue (and ?y (not (== eof)))))))
(ImmediateVector2 ?x ?y))
(_ eof)))
(define parse-ImmediateVector2!
(parse-success-or-error 'parse-ImmediateVector2 parse-ImmediateVector2))
(struct
ImmediateVector3
(x y z)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match
preservable
((ImmediateVector3 ?x ?y ?z)
(record
'v
(list (*->preserve ?x) (*->preserve ?y) (*->preserve ?z))))))))
(define (parse-ImmediateVector3 input)
(match
input
((and dest
(record
'v
(list
(app parse-DoubleValue (and ?x (not (== eof))))
(app parse-DoubleValue (and ?y (not (== eof))))
(app parse-DoubleValue (and ?z (not (== eof)))))))
(ImmediateVector3 ?x ?y ?z))
(_ eof)))
(define parse-ImmediateVector3!
(parse-success-or-error 'parse-ImmediateVector3 parse-ImmediateVector3))
(struct
Light
(v)
@ -364,6 +473,37 @@
(Light ?v))
(_ eof)))
(define parse-Light! (parse-success-or-error 'parse-Light parse-Light))
(struct
LiteralVector3
(x y z)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match
preservable
((LiteralVector3 ?x ?y ?z)
(record
'v
(list
(exact->inexact (*->preserve ?x))
(exact->inexact (*->preserve ?y))
(exact->inexact (*->preserve ?z)))))))))
(define (parse-LiteralVector3 input)
(match
input
((and dest
(record
'v
(list
(and ?x (? flonum?))
(and ?y (? flonum?))
(and ?z (? flonum?)))))
(LiteralVector3 ?x ?y ?z))
(_ eof)))
(define parse-LiteralVector3!
(parse-success-or-error 'parse-LiteralVector3 parse-LiteralVector3))
(define (Mesh? p)
(or (Mesh-Sphere? p)
(Mesh-Box? p)
@ -508,36 +648,32 @@
(define (parse-Plane input)
(match input ((and dest (record 'plane (list))) (Plane)) (_ eof)))
(define parse-Plane! (parse-success-or-error 'parse-Plane parse-Plane))
(define (Quaternion? p)
(or (Quaternion-immediate? p) (Quaternion-reference? p)))
(struct
Quaternion
(a b c d)
Quaternion-immediate
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match
preservable
((Quaternion ?a ?b ?c ?d)
(record
'q
(list
(exact->inexact (*->preserve ?a))
(exact->inexact (*->preserve ?b))
(exact->inexact (*->preserve ?c))
(exact->inexact (*->preserve ?d)))))))))
(match preservable ((Quaternion-immediate src) (*->preserve src))))))
(struct
Quaternion-reference
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match preservable ((Quaternion-reference src) (*->preserve src))))))
(define (parse-Quaternion input)
(match
input
((and dest
(record
'q
(list
(and ?a (? flonum?))
(and ?b (? flonum?))
(and ?c (? flonum?))
(and ?d (? flonum?)))))
(Quaternion ?a ?b ?c ?d))
((app parse-ImmediateQuaternion (and dest (not (== eof))))
(Quaternion-immediate dest))
((and dest (? symbol?)) (Quaternion-reference dest))
(_ eof)))
(define parse-Quaternion!
(parse-success-or-error 'parse-Quaternion parse-Quaternion))
@ -873,7 +1009,7 @@
(define parse-Sphere! (parse-success-or-error 'parse-Sphere parse-Sphere))
(struct
Sprite
(name shape)
(name formals shape)
#:transparent
#:methods
gen:preservable
@ -881,8 +1017,13 @@
(define (->preserve preservable)
(match
preservable
((Sprite ?name ?shape)
(record 'sprite (list (*->preserve ?name) (*->preserve ?shape))))))))
((Sprite ?name ?formals ?shape)
(record
'sprite
(list
(*->preserve ?name)
(for/list ((item (in-list ?formals))) (*->preserve item))
(*->preserve ?shape))))))))
(define (parse-Sprite input)
(match
input
@ -891,8 +1032,9 @@
'sprite
(list
(and ?name (? string?))
(list (and ?formals (? symbol?)) ...)
(app parse-Shape (and ?shape (not (== eof)))))))
(Sprite ?name ?shape))
(Sprite ?name ?formals ?shape))
(_ eof)))
(define parse-Sprite! (parse-success-or-error 'parse-Sprite parse-Sprite))
(struct
@ -962,7 +1104,7 @@
(*->preserve ?path)
(*->preserve ?scale)
(*->preserve ?offset)
(exact->inexact (*->preserve ?alpha))))))))
(*->preserve ?alpha)))))))
(define (parse-TextureSpec input)
(match
input
@ -978,7 +1120,7 @@
(and ?path (? string?))
(app parse-Vector2 (and ?scale (not (== eof))))
(app parse-Vector2 (and ?offset (not (== eof))))
(and ?alpha (? flonum?))))
(app parse-DoubleValue (and ?alpha (not (== eof))))))
(TextureSpec-uvAlpha ?path ?scale ?offset ?alpha))
(_ eof)))
(define parse-TextureSpec!
@ -1006,8 +1148,8 @@
(define parse-Touchable!
(parse-success-or-error 'parse-Touchable parse-Touchable))
(struct
Vector2
(x y)
Variable
(spriteName variable value)
#:transparent
#:methods
gen:preservable
@ -1015,47 +1157,79 @@
(define (->preserve preservable)
(match
preservable
((Vector2 ?x ?y)
((Variable ?spriteName ?variable ?value)
(record
'v
'variable
(list
(exact->inexact (*->preserve ?x))
(exact->inexact (*->preserve ?y)))))))))
(define (parse-Vector2 input)
(match
input
((and dest (record 'v (list (and ?x (? flonum?)) (and ?y (? flonum?)))))
(Vector2 ?x ?y))
(_ eof)))
(define parse-Vector2! (parse-success-or-error 'parse-Vector2 parse-Vector2))
(struct
Vector3
(x y z)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match
preservable
((Vector3 ?x ?y ?z)
(record
'v
(list
(exact->inexact (*->preserve ?x))
(exact->inexact (*->preserve ?y))
(exact->inexact (*->preserve ?z)))))))))
(define (parse-Vector3 input)
(*->preserve ?spriteName)
(*->preserve ?variable)
(*->preserve ?value))))))))
(define (parse-Variable input)
(match
input
((and dest
(record
'v
'variable
(list
(and ?x (? flonum?))
(and ?y (? flonum?))
(and ?z (? flonum?)))))
(Vector3 ?x ?y ?z))
(and ?spriteName (? string?))
(and ?variable (? symbol?))
?value)))
(Variable ?spriteName ?variable ?value))
(_ eof)))
(define parse-Variable!
(parse-success-or-error 'parse-Variable parse-Variable))
(define (Vector2? p) (or (Vector2-immediate? p) (Vector2-reference? p)))
(struct
Vector2-immediate
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match preservable ((Vector2-immediate src) (*->preserve src))))))
(struct
Vector2-reference
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match preservable ((Vector2-reference src) (*->preserve src))))))
(define (parse-Vector2 input)
(match
input
((app parse-ImmediateVector2 (and dest (not (== eof))))
(Vector2-immediate dest))
((and dest (? symbol?)) (Vector2-reference dest))
(_ eof)))
(define parse-Vector2! (parse-success-or-error 'parse-Vector2 parse-Vector2))
(define (Vector3? p) (or (Vector3-immediate? p) (Vector3-reference? p)))
(struct
Vector3-immediate
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match preservable ((Vector3-immediate src) (*->preserve src))))))
(struct
Vector3-reference
(value)
#:transparent
#:methods
gen:preservable
((define/generic *->preserve ->preserve)
(define (->preserve preservable)
(match preservable ((Vector3-reference src) (*->preserve src))))))
(define (parse-Vector3 input)
(match
input
((app parse-ImmediateVector3 (and dest (not (== eof))))
(Vector3-immediate dest))
((and dest (? symbol?)) (Vector3-reference dest))
(_ eof)))
(define parse-Vector3!
(parse-success-or-error 'parse-Vector3 parse-Vector3)))

View File

@ -1,12 +1,12 @@
version 1 .
Portal = <portal @name string @destination PortalDestination @position shapes.Vector3> .
Portal = <portal @name string @destination PortalDestination @position shapes.LiteralVector3> .
PortalDestination =
/ @local #!any
/ @remote noise.Route
.
Gravity = <gravity @direction shapes.Vector3> .
Gravity = <gravity @direction shapes.LiteralVector3> .
AmbientSound = <ambient-sound @name string @spec shapes.SoundSpec> .

View File

@ -1,6 +1,7 @@
version 1 .
Sprite = <sprite @name string @shape Shape> .
Sprite = <sprite @name string @formals [symbol ...] @shape Shape> .
Variable = <variable @spriteName string @variable symbol @value any> .
Shape = Mesh / Light / Scale / Move / Rotate / @many [Shape ...] / Texture / Color / Sound / Name / Floor / Nonphysical / Touchable / CSG / Skybox .
@ -8,15 +9,23 @@ Mesh = Sphere / Box / Ground / Plane / External .
Sphere = <sphere> .
Box = <box> .
Ground = <ground @size Vector2> .
Ground = <ground> .
Plane = <plane> .
External = <external @path string> .
Light = <hemispheric-light @v Vector3> .
Vector2 = <v @x double @y double> .
Vector3 = <v @x double @y double @z double> .
Quaternion = <q @a double @b double @c double @d double> .
DoubleValue = @immediate double / @reference symbol .
ImmediateVector2 = <v @x DoubleValue @y DoubleValue> .
ImmediateVector3 = <v @x DoubleValue @y DoubleValue @z DoubleValue> .
ImmediateQuaternion = <q @a DoubleValue @b DoubleValue @c DoubleValue @d DoubleValue> .
LiteralVector3 = <v @x double @y double @z double> .
Vector2 = @immediate ImmediateVector2 / @reference symbol .
Vector3 = @immediate ImmediateVector3 / @reference symbol .
Quaternion = @immediate ImmediateQuaternion / @reference symbol .
Scale = <scale @v Vector3 @shape Shape> .
Move = <move @v Vector3 @shape Shape> .
@ -26,12 +35,12 @@ Texture = <texture @spec TextureSpec @shape Shape> .
TextureSpec =
/ @simple [@path string]
/ @uv [@path string @scale Vector2 @offset Vector2]
/ @uvAlpha [@path string @scale Vector2 @offset Vector2 @alpha double]
/ @uvAlpha [@path string @scale Vector2 @offset Vector2 @alpha DoubleValue]
.
Color =
/ @opaque <color @r double @g double @b double @shape Shape>
/ @transparent <color @r double @g double @b double @alpha double @shape Shape>
/ @opaque <color @r DoubleValue @g DoubleValue @b DoubleValue @shape Shape>
/ @transparent <color @r DoubleValue @g DoubleValue @b DoubleValue @alpha DoubleValue @shape Shape>
.
Sound = <sound @spec SoundSpec @shape Shape> .
@ -50,9 +59,9 @@ CSG = <csg @expr CSGExpr> .
CSGExpr =
/ <mesh @shape Mesh>
/ <scale @v Vector3 @shape CSGExpr>
/ <move @v Vector3 @shape CSGExpr>
/ <rotate @v Vector3 @shape CSGExpr>
/ <scale @v LiteralVector3 @shape CSGExpr>
/ <move @v LiteralVector3 @shape CSGExpr>
/ <rotate @v LiteralVector3 @shape CSGExpr>
/ <subtract [@base CSGExpr @more CSGExpr ...]>
/ <union [@base CSGExpr @more CSGExpr ...]>
/ <intersect [@base CSGExpr @more CSGExpr ...]>

View File

@ -1,14 +1,14 @@
<sprite "light" <hemispheric-light <v 0.1 1.0 0.0>>>
<sprite "light" [] <hemispheric-light <v 0.1 1.0 0.0>>>
<gravity <v 0.0 -9.81 0.0>>
<sprite "sky" <skybox "textures/Daylight Box UV_0">>
<sprite "sky" [] <skybox "textures/Daylight Box UV_0">>
<sprite "ground"
<sprite "ground" []
<texture ["textures/grass-256x256.jpg"
<v 100.0 100.0>
<v 0.0 0.0>]
<floor <ground <v 300.0 300.0>>>>>
<floor <scale <v 300.0 1.0 300.0> <ground>>>>>
<sprite "box"
<sprite "box" []
<move <v -6.0 1.0 8.0>
<scale <v 10.0 0.1 10.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
@ -17,20 +17,20 @@
0.75]
<floor <box>>>>>>
<sprite "box2"
<sprite "box2" []
<move <v -500.0 0.5 3.0>
<floor
<color 0.0 0.0 1.0 1.0
<scale <v 1000.0 1.0 1.0> <box>>>>>>
<Exit "p" "lobby">
<sprite "p"
<sprite "p" []
<move <v -999.5 2.0 3.0>
<rotate <v 0.0 -0.25 0.0>
<color 1.0 0.0 1.0
<touchable <scale <v 1.0 2.0 1.0> <plane>>>>>>>
<sprite "steps"
<sprite "steps" []
<color 0.0 0.5 0.0
<move <v 0.0 0.0 3.5>
<scale <v 1.0 1.0 3.0>
@ -42,8 +42,9 @@
]>>>>>
<Exit "door" "other">
<sprite "door"
<move <v -3.0 0.0 1.5>
<variable "door" y 0.0>
<sprite "door" [y]
<move <v -3.0 y 1.5>
<rotate <v 0.0 -0.1 0.0> [
<scale <v 1.0 2.11 1.0>
<texture ["textures/door1.jpg"]
@ -52,7 +53,7 @@
]>>>
<portal "door2" <route [<ws "wss://vr.demo.eighty-twenty.org/ws">]>>
<sprite "door2"
<sprite "door2" []
<move <v -5.0 0.0 1.5>
<rotate <v 0.0 -0.1 0.0> [
<scale <v 1.0 2.11 1.0>
@ -61,14 +62,14 @@
<scale <v 1.0 2.11 0.1> <move <v 0.0 0.5 0.6> <box>>>
]>>>
<sprite "plans"
<sprite "plans" []
<texture ["plans/signal-2022-12-27-125451_002.jpeg"]
<move <v 0.0 1.0 -10.0>
<rotate <v 0.1 0.0 0.0>
<scale <v 2.0 2.0 0.1>
<box>>>>>>
<sprite "tower"
<sprite "tower" []
<rotate <v 0.1 0.0 0.0>
<move <v -10.0 50.0 13.0>
<scale <v 3.0 100.0 3.0>
@ -76,7 +77,7 @@
<color 0.5 0.5 0.0
<box>>>>>>>
; <sprite "sponge"
; <sprite "sponge" []
; <move <v 4.0 1.6 0.0> [
; <scale <v 0.001 0.001 0.001>
; <external "objects/usnm_346-100k-obj/usnm_346-01-100k.obj">>
@ -86,12 +87,12 @@
; <box>>>>
; ]>>
; <sprite "sofa"
; <sprite "sofa" []
; <rotate <v -0.125 0.5 0.0>
; <scale <v 0.01 0.01 0.01>
; <external "objects/IKE020001_obj/IKEA-Arild_2_Seat_Sofa-3D.obj">>>>
<sprite "x"
<sprite "x" []
<move <v 10.0 1.6 -5.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
<v 1.0 3.0 1.0>
@ -105,7 +106,7 @@
>>>>>
; <Exit "y" "lobby">
; <sprite "y"
; <sprite "y" []
; <move <v 12.0 0.75 -5.0>
; <texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
; <v 1.0 1.0 1.0>

View File

@ -1,6 +1,6 @@
<sprite "light" <hemispheric-light <v 0.1 1.0 0.0>>>
<sprite "light" [] <hemispheric-light <v 0.1 1.0 0.0>>>
;<gravity <v 0.0 -9.81 0.0>>
<sprite "sky" <skybox "textures/eso0932a">>
<sprite "sky" [] <skybox "textures/eso0932a">>
<ambient-sound "space" <loop "sounds/Space-atmosphere-sound/Space-atmosphere-sound.mp3">>
@ -11,7 +11,7 @@
; <floor <ground <v 300.0 300.0>>>>>
<Exit "home" "lobby" <v -2.5 0.0 0.5>>
<sprite "home"
<sprite "home" []
<move <v 0.0 0.0 -2.0>
<rotate <v 0.0 -0.5 0.0> [
<scale <v 1.0 2.11 1.0>
@ -20,7 +20,7 @@
<scale <v 1.0 2.11 0.1> <move <v 0.0 0.5 0.6> <box>>>
]>>>
<sprite "x"
<sprite "x" []
<move <v 0.0 0.0 10.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
<v 1.0 3.0 1.0>

View File

@ -1,4 +1,4 @@
import { is, fromJS, Dataflow, Dataspace, Embedded, Reader, Ref, Schemas, Sturdy, Turn } from "@syndicate-lang/core";
import { is, fromJS, Dataflow, Dataspace, Embedded, Ref, Schemas, Turn } from "@syndicate-lang/core";
import * as html from "@syndicate-lang/html";
import * as timer from "@syndicate-lang/timer";
import * as wsRelay from "@syndicate-lang/ws-relay";
@ -14,7 +14,7 @@ import {
Vector3,
} from '@babylonjs/core/Legacy/legacy';
import { activeFloorMeshes, activeTouchableMeshes, ShapeTree, v3, scale3, buildSound, builder as B } from './shapes.js';
import { activeFloorMeshes, activeTouchableMeshes, Environment, ShapeTree, buildSound, builder as B, u2, u3, u3v } from './shapes.js';
import { RunningEngine } from './engine.js';
import { uuid } from './uuid.js';
@ -22,13 +22,14 @@ assertion type SceneHandle(ds: Embedded<Ref>);
function interpretScene(myId: string, runningEngine: RunningEngine, rootMesh: Mesh, sceneDs: Ref) {
at sceneDs {
during Shapes.Sprite({ "name": $name: string }) => spawn named `sprite:${name}` {
during Shapes.Sprite({ "name": $name: string, "formals": $formals }) => spawn named `sprite:${name}` {
if (name === myId) {
console.log('ignoring sprite', name);
} else {
console.log('+shape', name);
on stop console.log('-shape', name);
spriteMain(name, runningEngine, rootMesh, sceneDs);
const env = new Environment(name, formals as symbol[], sceneDs);
spriteMain(env, runningEngine, rootMesh);
}
}
@ -42,20 +43,20 @@ function interpretScene(myId: string, runningEngine: RunningEngine, rootMesh: Me
}
}
function spriteMain(spriteName: string, runningEngine: RunningEngine, rootMesh: Mesh, sceneDs: Ref) {
at sceneDs {
let currentShape = ShapeTree.empty(spriteName, runningEngine.scene);
function spriteMain(env: Environment, runningEngine: RunningEngine, rootMesh: Mesh) {
at env.sceneDs {
let currentShape = ShapeTree.empty(env.spriteName, runningEngine.scene);
currentShape.rootnode.parent = rootMesh;
on stop currentShape.remove();
during Shapes.Sprite({ "name": spriteName, "shape": $shape: Shapes.Shape }) => {
currentShape = currentShape.reconcile(spriteName, spriteName, shape);
during Shapes.Sprite({ "name": env.spriteName, "shape": $shape: Shapes.Shape }) => {
currentShape = currentShape.reconcile(env, env.spriteName, shape);
currentShape.rootnode.parent = rootMesh;
}
during SceneProtocol.Gravity($direction: Shapes.Vector3) => {
during SceneProtocol.Gravity($direction: Shapes.LiteralVector3) => {
runningEngine.applyGravity = true;
on stop runningEngine.applyGravity = false;
runningEngine.gravity = v3(direction);
runningEngine.gravity = new Vector3(direction.x, direction.y, direction.z);
}
}
}
@ -101,21 +102,23 @@ async function enterScene(
}
};
const currentPosition = () => Shapes.Vector3(runningEngine.position);
const currentRotation = () => Shapes.Vector3(runningEngine.rotation);
const TAU = 2 * Math.PI;
field position: Shapes.Vector3 = currentPosition();
field rotation: Shapes.Vector3 = currentRotation();
const currentPosition = () => u3(runningEngine.position);
const currentRotation = () => u3(runningEngine.rotation, 1/TAU);
field position: Shapes.ImmediateVector3 = currentPosition();
field rotation: Shapes.ImmediateVector3 = currentRotation();
const refreshPeriod = Math.floor(1000 / 10);
at ds {
on message timer.PeriodicTick(refreshPeriod) => {
const newPosition = currentPosition();
const newRotation = currentRotation();
if (!is(Shapes.fromVector3(position.value), Shapes.fromVector3(newPosition))) {
if (!is(Shapes.fromImmediateVector3(position.value), Shapes.fromImmediateVector3(newPosition))) {
position.value = newPosition;
}
if (!is(Shapes.fromVector3(rotation.value), Shapes.fromVector3(newRotation))) {
if (!is(Shapes.fromImmediateVector3(rotation.value), Shapes.fromImmediateVector3(newRotation))) {
rotation.value = newRotation;
}
}
@ -128,7 +131,7 @@ async function enterScene(
on asserted SceneProtocol.Portal({
"name": o,
"destination": $dest: SceneProtocol.PortalDestination,
"position": $targetPosition: Shapes.Vector3,
"position": $targetPosition: Shapes.LiteralVector3,
}) => {
const newPos = new Vector3(targetPosition.x,
targetPosition.y,
@ -169,29 +172,46 @@ async function enterScene(
}
}
assert Shapes.Sprite({
const _POS = Symbol.for('pos');
const _HEAD = Symbol.for('head');
const _BODY = Symbol.for('body');
assert Shapes.Variable({ spriteName: id, variable: _POS, value: Shapes.fromImmediateVector3(position.value) });
assert Shapes.Variable({ spriteName: id, variable: _HEAD, value: Shapes.fromImmediateVector3(rotation.value) });
assert Shapes.Variable({ spriteName: id, variable: _BODY, value: Shapes.fromImmediateVector3({
x: Shapes.DoubleValue.immediate(0),
y: rotation.value.y,
z: Shapes.DoubleValue.immediate(0),
}) });
assert (() => {
const s = Shapes.Sprite({
name: id,
formals: [_POS, _HEAD, _BODY],
shape: B.nonphysical(
B.move(position.value, B.many([
B.move({ x: 0, y: -0.9, z: 0 },
B.rotate(scale3({ x: 0, y: rotation.value.y, z: 0 }, 1 / (2 * Math.PI)),
B.scale({ x: 0.4, y: 1.4, z: 0.1 }, B.box()))),
B.rotate(scale3(rotation.value, 1 / (2 * Math.PI)),
B.scale({ x: 0.15, y: 0.23, z: 0.18 }, B.many([
B.move(Shapes.Vector3.reference(_POS), B.many([
B.move(u3v({x: 0, y: -0.9, z: 0}),
B.rotate(Shapes.Vector3.reference(_BODY),
B.scale(u3v({x: 0.4, y: 1.4, z: 0.1}), B.box()))),
B.rotate(Shapes.Vector3.reference(_HEAD),
B.scale(u3v({x: 0.15, y: 0.23, z: 0.18}), B.many([
B.box(),
B.move({ x: 0, y: 0, z: 0.501 },
B.move(u3v({x: 0, y: 0, z: 0.501}),
B.texture(
Shapes.TextureSpec.uvAlpha({
path: `https://www.gravatar.com/avatar/${md5(new TextEncoder().encode(email.value.trim()))}?s=256&d=wavatar`,
scale: Shapes.Vector2({ x:1, y:1 }),
offset: Shapes.Vector2({ x:0, y:0 }),
alpha: 1
scale: Shapes.Vector2.immediate(u2({ x:1, y:1 })),
offset: Shapes.Vector2.immediate(u2({ x:0, y:0 })),
alpha: Shapes.DoubleValue.immediate(1),
}),
B.rotate({ x: 0, y: 0.5, z: 0 },
B.rotate(u3v({ x: 0, y: 0.5, z: 0 }),
B.plane()))),
]))),
]))),
});
});
console.log(s);
return s;
})();
}
}
@ -265,7 +285,7 @@ window.addEventListener('load', async () => {
Dataspace.boot(ds => {
html.boot(ds);
timer.boot(ds);
wsRelay.boot(ds, false);
wsRelay.boot(ds, true);
wakeDetector.boot(ds);
bootApp(ds, runningEngine);
});

View File

@ -20,10 +20,46 @@ import {
Vector2,
Vector3,
} from '@babylonjs/core/Legacy/legacy';
import { KeyedDictionary, Value, is } from "@syndicate-lang/core";
import { Dataflow, IdentityMap, KeyedDictionary, Ref, Value, is } from "@syndicate-lang/core";
import * as Shapes from './gen/shapes.js';
export class Environment {
fields = new IdentityMap<symbol, Dataflow.Field<Value<Ref>>>();
constructor(
public spriteName: string,
public formals: symbol[],
public sceneDs: Ref,
) {
formals.forEach(f => {
field v: Value<Ref> = false;
at this.sceneDs {
on asserted Shapes.Variable({
"spriteName": this.spriteName,
"variable": f,
"value": $newValue,
}) => {
console.log('Got value for', spriteName, f, newValue);
v.value = newValue;
}
}
this.fields.set(f, v);
});
}
lookup(f: symbol, k: (v: Value<Ref>) => void) {
dataflow {
const v = this.fields.get(f);
if (v === void 0) throw new Error(`Lookup of ${f.description} in ${this.spriteName} failed`);
const w = v.value;
if (w !== false) k(w);
}
}
}
export type ValueK = (v: Value<Ref>) => void;
export const activeFloorMeshes: Array<AbstractMesh> = [];
export const activeTouchableMeshes: Array<AbstractMesh> = [];
@ -50,13 +86,13 @@ export class ShapeTree<N extends Node = Node> {
return this.subnodes.then(ns => [this.rootnode, ... ns]);
}
reconcile(spriteName: string, name: string, shape: Shapes.Shape): ShapeTree {
reconcile(env: Environment, name: string, shape: Shapes.Shape): ShapeTree {
if (is(Shapes.fromShape(shape), this.shapePreserve)) {
return this;
} else {
this.remove();
return build(name, this.scene, shape, {
spriteName: async m => m.rootnode.metadata.spriteName = spriteName,
return build(env, name, this.scene, shape, {
spriteName: async m => m.rootnode.metadata.spriteName = env.spriteName,
collisions: async m => (await m.allnodes).forEach(n => n.checkCollisions = true),
});
}
@ -82,20 +118,64 @@ export class ShapeTree<N extends Node = Node> {
}
}
export function v2(v: Shapes.Vector2): Vector2 {
return new Vector2(v.x, v.y);
export function dv(env: Environment, v: Shapes.DoubleValue, k: (v: number) => void) {
switch (v._variant) {
case "immediate": k(v.value); break;
case "reference": env.lookup(v.value, k as ValueK); break;
}
}
export function v3(v: Shapes.Vector3): Vector3 {
return new Vector3(v.x, v.y, v.z);
export function u2(v: {x: number, y: number}): Shapes.ImmediateVector2 {
return Shapes.ImmediateVector2({
x: Shapes.DoubleValue.immediate(v.x),
y: Shapes.DoubleValue.immediate(v.y),
});
}
export function scale3(v: Shapes.Vector3, scale: number): Shapes.Vector3 {
return Shapes.Vector3({ x: v.x * scale, y: v.y * scale, z: v.z * scale });
export function lv2(env: Environment, v: Shapes.ImmediateVector2, k: (v: Vector2) => void) {
dv(env, v.x, x => dv(env, v.y, y => k(new Vector2(x, y))));
}
export function q(q: Shapes.Quaternion): Quaternion {
return new Quaternion(q.a, q.b, q.c, q.d);
export function v2(env: Environment, v: Shapes.Vector2, k: (v: Vector2) => void) {
switch (v._variant) {
case "immediate": lv2(env, v.value, k); break;
case "reference": env.lookup(v.value, v => lv2(env, Shapes.asImmediateVector2(v), k)); break;
}
}
export function u3(v: {x: number, y: number, z: number}, scale = 1): Shapes.ImmediateVector3 {
return Shapes.ImmediateVector3({
x: Shapes.DoubleValue.immediate(v.x * scale),
y: Shapes.DoubleValue.immediate(v.y * scale),
z: Shapes.DoubleValue.immediate(v.z * scale),
});
}
export function u3v(v: {x: number, y: number, z: number}, scale = 1): Shapes.Vector3 {
return Shapes.Vector3.immediate(u3(v, scale));
}
export function lv3(env: Environment, v: Shapes.ImmediateVector3, k: (v: Vector3) => void) {
dv(env, v.x, x => dv(env, v.y, y => dv(env, v.z, z => k(new Vector3(x, y, z)))));
}
export function v3(env: Environment, v: Shapes.Vector3, k: (v: Vector3) => void) {
switch (v._variant) {
case "immediate": lv3(env, v.value, k); break;
case "reference": env.lookup(v.value, v => lv3(env, Shapes.asImmediateVector3(v), k)); break;
}
}
export function lq(env: Environment, q: Shapes.ImmediateQuaternion, k: (v: Quaternion) => void) {
dv(env, q.a, a => dv(env, q.b, b => dv(env, q.c, c => dv(env, q.d, d =>
k(new Quaternion(a, b, c, d))))));
}
export function q(env: Environment, q: Shapes.Quaternion, k: (v: Quaternion) => void) {
switch (q._variant) {
case "immediate": lq(env, q.value, k); break;
case "reference": env.lookup(q.value, q => lq(env, Shapes.asImmediateQuaternion(q), k)); break;
}
}
export type MeshCustomizer = { [key: string]: ((m: ShapeTree<AbstractMesh>) => void) };
@ -111,7 +191,7 @@ type CachedTexture = {
};
const textureCache = new KeyedDictionary<Value, CachedTexture>();
function buildTexture(name: string, scene: Scene, spec: Shapes.TextureSpec): CachedTexture {
function buildTexture(env: Environment, name: string, scene: Scene, spec: Shapes.TextureSpec): CachedTexture {
const cacheKey = Shapes.fromTextureSpec(spec);
const entry = textureCache.get(cacheKey);
if (entry !== void 0) {
@ -125,18 +205,18 @@ function buildTexture(name: string, scene: Scene, spec: Shapes.TextureSpec): Cac
case "simple":
break;
case "uvAlpha":
mat.alpha = spec.alpha;
mat.alpha = 0;
dv(env, spec.alpha, a => mat.alpha = a);
tex.hasAlpha = true;
/* FALL THROUGH */
case "uv": {
const scale = v2(spec.scale);
const offset = v2(spec.offset);
tex.uScale = scale.x;
tex.vScale = scale.y;
tex.uOffset = offset.x;
tex.vOffset = offset.y;
case "uv":
v2(env, spec.scale, scale => v2(env, spec.offset, offset => {
tex.uScale = scale.x;
tex.vScale = scale.y;
tex.uOffset = offset.x;
tex.vOffset = offset.y;
}));
break;
}
}
const newEntry = {
key: cacheKey,
@ -167,13 +247,7 @@ export function buildMesh(
switch (meshSpec._variant) {
case "Sphere": return { rootnode: MeshBuilder.CreateSphere(name, {}, scene) };
case "Box": return { rootnode: MeshBuilder.CreateBox(name, {}, scene) };
case "Ground": {
const v = v2(meshSpec.value.size);
return {
rootnode: MeshBuilder.CreateGround(
name, { width: v.x, height: v.y }, scene ?? void 0),
};
}
case "Ground": return { rootnode: MeshBuilder.CreateGround(name, {}, scene ?? void 0) };
case "Plane": return { rootnode: MeshBuilder.CreatePlane(name, {}, scene) };
case "External": {
const rootnode = new Mesh(name, scene);
@ -210,7 +284,7 @@ export function buildSound(name: string, scene: Scene, spec: Shapes.SoundSpec, s
return new Sound(name, spec.url, scene, null, options);
}
export function build(name: string, scene: Scene, shape: Shapes.Shape, customize: MeshCustomizer): ShapeTree {
export function build(env: Environment, name: string, scene: Scene, shape: Shapes.Shape, customize: MeshCustomizer): ShapeTree {
switch (shape._variant) {
case "Mesh": {
const m = buildMesh(name, scene, shape.value);
@ -219,23 +293,23 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
return t;
}
case "Light":
return new ShapeTree(
scene,
shape,
new HemisphericLight(name, v3(shape.value.v), scene));
case "Light": {
const light = new HemisphericLight(name, new Vector3(0, 1, 0), scene);
v3(env, shape.value.v, v => light.direction = v);
return new ShapeTree(scene, shape, light);
}
case "Scale": {
const t = ShapeTree.transform(name, scene, shape);
t.rootnode.scaling = v3(shape.value.v);
build(name + '.inner', scene, shape.value.shape, customize).parent = t;
v3(env, shape.value.v, v => t.rootnode.scaling = v);
build(env, name + '.inner', scene, shape.value.shape, customize).parent = t;
return t;
}
case "Move": {
const t = ShapeTree.transform(name, scene, shape);
t.rootnode.position = v3(shape.value.v);
build(name + '.inner', scene, shape.value.shape, customize).parent = t;
v3(env, shape.value.v, v => t.rootnode.position = v);
build(env, name + '.inner', scene, shape.value.shape, customize).parent = t;
return t;
}
@ -243,28 +317,30 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
const t = ShapeTree.transform(name, scene, shape);
switch (shape.value._variant) {
case "euler":
t.rootnode.rotation = v3(shape.value.v);
t.rootnode.rotation.scaleInPlace(2 * Math.PI);
v3(env, shape.value.v, v => {
t.rootnode.rotation = v;
t.rootnode.rotation.scaleInPlace(2 * Math.PI);
});
break;
case "quaternion":
t.rootnode.rotationQuaternion = q(shape.value.q);
q(env, shape.value.q, q => t.rootnode.rotationQuaternion = q);
break;
}
build(name + '.inner', scene, shape.value.shape, customize).parent = t;
build(env, name + '.inner', scene, shape.value.shape, customize).parent = t;
return t;
}
case "many": {
const t = ShapeTree.transform(name, scene, shape);
shape.value.forEach((s, i) => {
build(name + '[' + i + ']', scene, s, customize).parent = t;
build(env, name + '[' + i + ']', scene, s, customize).parent = t;
});
return t;
}
case "Texture": {
const entry = buildTexture(name + '.texture', scene, shape.value.spec);
const t = build(name + '.inner', scene, shape.value.shape, {
const entry = buildTexture(env, name + '.texture', scene, shape.value.spec);
const t = build(env, name + '.inner', scene, shape.value.shape, {
... customize,
material: async m => (await m.allnodes).forEach(n => n.material = entry.material),
});
@ -274,9 +350,13 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
case "Color": {
const mat = new StandardMaterial(name + '.texture', scene);
mat.diffuseColor = new Color3(shape.value.r, shape.value.g, shape.value.b);
if (shape.value._variant === "transparent") mat.alpha = shape.value.alpha;
const t = build(name + '.inner', scene, shape.value.shape, {
dv(env, shape.value.r, r =>
dv(env, shape.value.g, g =>
dv(env, shape.value.b, b => mat.diffuseColor = new Color3(r, g, b))));
if (shape.value._variant === "transparent") {
dv(env, shape.value.alpha, a => mat.alpha = a);
}
const t = build(env, name + '.inner', scene, shape.value.shape, {
... customize,
material: async m => (await m.allnodes).forEach(n => n.material = mat),
});
@ -286,7 +366,7 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
case "Sound": {
const sound = buildSound(name + ".sound", scene, shape.value.spec, true);
const t = build(name + ".inner", scene, shape.value.shape, {
const t = build(env, name + ".inner", scene, shape.value.shape, {
... customize,
sound: async m => sound.attachToMesh(m.rootnode),
});
@ -295,10 +375,10 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
}
case "Name":
return build(name + '.' + shape.value.base, scene, shape.value.shape, customize);
return build(env, name + '.' + shape.value.base, scene, shape.value.shape, customize);
case "Floor":
return build(name, scene, shape.value.shape, {
return build(env, name, scene, shape.value.shape, {
... customize,
floor: async m => {
const nodes = await m.allnodes;
@ -311,13 +391,13 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
});
case "Nonphysical":
return build(name, scene, shape.value.shape, {
return build(env, name, scene, shape.value.shape, {
... customize,
collisions: async m => (await m.allnodes).forEach(n => n.checkCollisions = false),
});
case "Touchable":
return build(name, scene, shape.value.shape, {
return build(env, name, scene, shape.value.shape, {
... customize,
touchable: async m => {
const nodes = await m.allnodes;
@ -426,23 +506,30 @@ export function buildCSG(name: string, scene: Scene, expr: Shapes.CSGExpr): Buil
};
}
export const builder: { [key: string]: (... args: any[]) => Shapes.Shape } = {
export const builder = {
sphere: () => Shapes.Shape.Mesh(Shapes.Mesh.Sphere(Shapes.Sphere())),
box: () => Shapes.Shape.Mesh(Shapes.Mesh.Box(Shapes.Box())),
plane: () => Shapes.Shape.Mesh(Shapes.Mesh.Plane(Shapes.Plane())),
ground: (v: Shapes.Vector2) => Shapes.Shape.Mesh(Shapes.Mesh.Ground(Shapes.Ground(Shapes.Vector2(v)))),
light: (v: Shapes.Vector3) => Shapes.Shape.Light(Shapes.Light(Shapes.Vector3(v))),
scale: (v: Shapes.Vector3, shape: Shapes.Shape) => Shapes.Shape.Scale(Shapes.Scale({ v: Shapes.Vector3(v), shape })),
move: (v: Shapes.Vector3, shape: Shapes.Shape) => Shapes.Shape.Move(Shapes.Move({ v: Shapes.Vector3(v), shape })),
rotate: (v: Shapes.Vector3, shape: Shapes.Shape) => Shapes.Shape.Rotate(Shapes.Rotate.euler({ v: Shapes.Vector3(v), shape })),
ground: () => Shapes.Shape.Mesh(Shapes.Mesh.Ground(Shapes.Ground())),
light: (v: Shapes.Vector3) => Shapes.Shape.Light(Shapes.Light(v)),
scale: (v: Shapes.Vector3, shape: Shapes.Shape) => Shapes.Shape.Scale(Shapes.Scale({ v, shape })),
move: (v: Shapes.Vector3, shape: Shapes.Shape) => Shapes.Shape.Move(Shapes.Move({ v, shape })),
rotate: (v: Shapes.Vector3, shape: Shapes.Shape) => Shapes.Shape.Rotate(Shapes.Rotate.euler({ v, shape })),
many: (shapes: Shapes.Shape[]) => Shapes.Shape.many(shapes),
texture: (spec: Shapes.TextureSpec, shape: Shapes.Shape) => Shapes.Shape.Texture(Shapes.Texture({ spec, shape })),
color: (r: number, g: number, b: number, shape: Shapes.Shape, alpha = 1.0) => {
return Shapes.Shape.Color((alpha === 1.0)
color: (r0: number | symbol, g0: number | symbol, b0: number | symbol, shape: Shapes.Shape, alpha0: number | symbol = 1.0) => {
function vd(x: number | symbol): Shapes.DoubleValue {
return typeof x === 'number' ? Shapes.DoubleValue.immediate(x) : Shapes.DoubleValue.reference(x);
}
const r = vd(r0);
const g = vd(g0);
const b = vd(b0);
const alpha = vd(alpha0);
return Shapes.Shape.Color((alpha0 === 1.0)
? Shapes.Color.opaque({ r, g, b, shape })
: Shapes.Color.transparent({ r, g, b, alpha, shape }));
},
name: (base: string, shape: Shapes.Shape) => Shapes.Shape.Name(Shapes.Name({ base, shape })),
floor: (shape: Shapes.Shape) => Shapes.Shape.Floor(Shapes.Floor(shape)),
nonphysical: (shape: Shapes.Shape) => Shapes.Shape.Nonphysical(Shapes.Nonphysical(shape)),
};
} satisfies { [key: string]: (... args: any[]) => Shapes.Shape };