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 (bouncy-box)
(define me (symbol->string (strong-gensym 'user))) (define me (symbol->string (strong-gensym 'user)))
(define start-time (current-inexact-milliseconds))
(define-field deadline start-time) (define x (* (- (random) 0.5) 100))
(define-field x (* (- (random) 0.5) 100))
(define y0 (+ 1.0 (* (random) 10))) (define y0 (+ 1.0 (* (random) 10)))
(define-field y y0) (define z (* (- (random) 0.5) 100))
(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 r (random)) (define r (random))
(define g (random)) (define g (random))
(define b (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 (at scene
(assert (Sprite me (Move (Vector3 (x) (y) (z)) (assert (Variable me 'y (y)))
(Color-opaque r g b (Box))))) (assert (Sprite me '(y) (Move (ImmediateVector3 x 'y z) (Color-opaque r g b (Box)))))))
))
(for [(i 100)] (bouncy-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)))))) #:import (lambda (v) (on-connected (embedded-value v))))))

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
version 1 . 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 . 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> . Sphere = <sphere> .
Box = <box> . Box = <box> .
Ground = <ground @size Vector2> . Ground = <ground> .
Plane = <plane> . Plane = <plane> .
External = <external @path string> . External = <external @path string> .
Light = <hemispheric-light @v Vector3> . Light = <hemispheric-light @v Vector3> .
Vector2 = <v @x double @y double> . DoubleValue = @immediate double / @reference symbol .
Vector3 = <v @x double @y double @z double> .
Quaternion = <q @a double @b double @c double @d double> . 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> . Scale = <scale @v Vector3 @shape Shape> .
Move = <move @v Vector3 @shape Shape> . Move = <move @v Vector3 @shape Shape> .
@ -26,12 +35,12 @@ Texture = <texture @spec TextureSpec @shape Shape> .
TextureSpec = TextureSpec =
/ @simple [@path string] / @simple [@path string]
/ @uv [@path string @scale Vector2 @offset Vector2] / @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 = Color =
/ @opaque <color @r double @g double @b double @shape Shape> / @opaque <color @r DoubleValue @g DoubleValue @b DoubleValue @shape Shape>
/ @transparent <color @r double @g double @b double @alpha double @shape Shape> / @transparent <color @r DoubleValue @g DoubleValue @b DoubleValue @alpha DoubleValue @shape Shape>
. .
Sound = <sound @spec SoundSpec @shape Shape> . Sound = <sound @spec SoundSpec @shape Shape> .
@ -50,9 +59,9 @@ CSG = <csg @expr CSGExpr> .
CSGExpr = CSGExpr =
/ <mesh @shape Mesh> / <mesh @shape Mesh>
/ <scale @v Vector3 @shape CSGExpr> / <scale @v LiteralVector3 @shape CSGExpr>
/ <move @v Vector3 @shape CSGExpr> / <move @v LiteralVector3 @shape CSGExpr>
/ <rotate @v Vector3 @shape CSGExpr> / <rotate @v LiteralVector3 @shape CSGExpr>
/ <subtract [@base CSGExpr @more CSGExpr ...]> / <subtract [@base CSGExpr @more CSGExpr ...]>
/ <union [@base CSGExpr @more CSGExpr ...]> / <union [@base CSGExpr @more CSGExpr ...]>
/ <intersect [@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>> <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" <texture ["textures/grass-256x256.jpg"
<v 100.0 100.0> <v 100.0 100.0>
<v 0.0 0.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> <move <v -6.0 1.0 8.0>
<scale <v 10.0 0.1 10.0> <scale <v 10.0 0.1 10.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg" <texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
@ -17,20 +17,20 @@
0.75] 0.75]
<floor <box>>>>>> <floor <box>>>>>>
<sprite "box2" <sprite "box2" []
<move <v -500.0 0.5 3.0> <move <v -500.0 0.5 3.0>
<floor <floor
<color 0.0 0.0 1.0 1.0 <color 0.0 0.0 1.0 1.0
<scale <v 1000.0 1.0 1.0> <box>>>>>> <scale <v 1000.0 1.0 1.0> <box>>>>>>
<Exit "p" "lobby"> <Exit "p" "lobby">
<sprite "p" <sprite "p" []
<move <v -999.5 2.0 3.0> <move <v -999.5 2.0 3.0>
<rotate <v 0.0 -0.25 0.0> <rotate <v 0.0 -0.25 0.0>
<color 1.0 0.0 1.0 <color 1.0 0.0 1.0
<touchable <scale <v 1.0 2.0 1.0> <plane>>>>>>> <touchable <scale <v 1.0 2.0 1.0> <plane>>>>>>>
<sprite "steps" <sprite "steps" []
<color 0.0 0.5 0.0 <color 0.0 0.5 0.0
<move <v 0.0 0.0 3.5> <move <v 0.0 0.0 3.5>
<scale <v 1.0 1.0 3.0> <scale <v 1.0 1.0 3.0>
@ -42,8 +42,9 @@
]>>>>> ]>>>>>
<Exit "door" "other"> <Exit "door" "other">
<sprite "door" <variable "door" y 0.0>
<move <v -3.0 0.0 1.5> <sprite "door" [y]
<move <v -3.0 y 1.5>
<rotate <v 0.0 -0.1 0.0> [ <rotate <v 0.0 -0.1 0.0> [
<scale <v 1.0 2.11 1.0> <scale <v 1.0 2.11 1.0>
<texture ["textures/door1.jpg"] <texture ["textures/door1.jpg"]
@ -52,7 +53,7 @@
]>>> ]>>>
<portal "door2" <route [<ws "wss://vr.demo.eighty-twenty.org/ws">]>> <portal "door2" <route [<ws "wss://vr.demo.eighty-twenty.org/ws">]>>
<sprite "door2" <sprite "door2" []
<move <v -5.0 0.0 1.5> <move <v -5.0 0.0 1.5>
<rotate <v 0.0 -0.1 0.0> [ <rotate <v 0.0 -0.1 0.0> [
<scale <v 1.0 2.11 1.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>>> <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"] <texture ["plans/signal-2022-12-27-125451_002.jpeg"]
<move <v 0.0 1.0 -10.0> <move <v 0.0 1.0 -10.0>
<rotate <v 0.1 0.0 0.0> <rotate <v 0.1 0.0 0.0>
<scale <v 2.0 2.0 0.1> <scale <v 2.0 2.0 0.1>
<box>>>>>> <box>>>>>>
<sprite "tower" <sprite "tower" []
<rotate <v 0.1 0.0 0.0> <rotate <v 0.1 0.0 0.0>
<move <v -10.0 50.0 13.0> <move <v -10.0 50.0 13.0>
<scale <v 3.0 100.0 3.0> <scale <v 3.0 100.0 3.0>
@ -76,7 +77,7 @@
<color 0.5 0.5 0.0 <color 0.5 0.5 0.0
<box>>>>>>> <box>>>>>>>
; <sprite "sponge" ; <sprite "sponge" []
; <move <v 4.0 1.6 0.0> [ ; <move <v 4.0 1.6 0.0> [
; <scale <v 0.001 0.001 0.001> ; <scale <v 0.001 0.001 0.001>
; <external "objects/usnm_346-100k-obj/usnm_346-01-100k.obj">> ; <external "objects/usnm_346-100k-obj/usnm_346-01-100k.obj">>
@ -86,12 +87,12 @@
; <box>>>> ; <box>>>>
; ]>> ; ]>>
; <sprite "sofa" ; <sprite "sofa" []
; <rotate <v -0.125 0.5 0.0> ; <rotate <v -0.125 0.5 0.0>
; <scale <v 0.01 0.01 0.01> ; <scale <v 0.01 0.01 0.01>
; <external "objects/IKE020001_obj/IKEA-Arild_2_Seat_Sofa-3D.obj">>>> ; <external "objects/IKE020001_obj/IKEA-Arild_2_Seat_Sofa-3D.obj">>>>
<sprite "x" <sprite "x" []
<move <v 10.0 1.6 -5.0> <move <v 10.0 1.6 -5.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg" <texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
<v 1.0 3.0 1.0> <v 1.0 3.0 1.0>
@ -105,7 +106,7 @@
>>>>> >>>>>
; <Exit "y" "lobby"> ; <Exit "y" "lobby">
; <sprite "y" ; <sprite "y" []
; <move <v 12.0 0.75 -5.0> ; <move <v 12.0 0.75 -5.0>
; <texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg" ; <texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
; <v 1.0 1.0 1.0> ; <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>> ;<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">> <ambient-sound "space" <loop "sounds/Space-atmosphere-sound/Space-atmosphere-sound.mp3">>
@ -11,7 +11,7 @@
; <floor <ground <v 300.0 300.0>>>>> ; <floor <ground <v 300.0 300.0>>>>>
<Exit "home" "lobby" <v -2.5 0.0 0.5>> <Exit "home" "lobby" <v -2.5 0.0 0.5>>
<sprite "home" <sprite "home" []
<move <v 0.0 0.0 -2.0> <move <v 0.0 0.0 -2.0>
<rotate <v 0.0 -0.5 0.0> [ <rotate <v 0.0 -0.5 0.0> [
<scale <v 1.0 2.11 1.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>>> <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> <move <v 0.0 0.0 10.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg" <texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"
<v 1.0 3.0 1.0> <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 html from "@syndicate-lang/html";
import * as timer from "@syndicate-lang/timer"; import * as timer from "@syndicate-lang/timer";
import * as wsRelay from "@syndicate-lang/ws-relay"; import * as wsRelay from "@syndicate-lang/ws-relay";
@ -14,7 +14,7 @@ import {
Vector3, Vector3,
} from '@babylonjs/core/Legacy/legacy'; } 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 { RunningEngine } from './engine.js';
import { uuid } from './uuid.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) { function interpretScene(myId: string, runningEngine: RunningEngine, rootMesh: Mesh, sceneDs: Ref) {
at sceneDs { 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) { if (name === myId) {
console.log('ignoring sprite', name); console.log('ignoring sprite', name);
} else { } else {
console.log('+shape', name); console.log('+shape', name);
on stop 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) { function spriteMain(env: Environment, runningEngine: RunningEngine, rootMesh: Mesh) {
at sceneDs { at env.sceneDs {
let currentShape = ShapeTree.empty(spriteName, runningEngine.scene); let currentShape = ShapeTree.empty(env.spriteName, runningEngine.scene);
currentShape.rootnode.parent = rootMesh; currentShape.rootnode.parent = rootMesh;
on stop currentShape.remove(); on stop currentShape.remove();
during Shapes.Sprite({ "name": spriteName, "shape": $shape: Shapes.Shape }) => { during Shapes.Sprite({ "name": env.spriteName, "shape": $shape: Shapes.Shape }) => {
currentShape = currentShape.reconcile(spriteName, spriteName, shape); currentShape = currentShape.reconcile(env, env.spriteName, shape);
currentShape.rootnode.parent = rootMesh; currentShape.rootnode.parent = rootMesh;
} }
during SceneProtocol.Gravity($direction: Shapes.Vector3) => { during SceneProtocol.Gravity($direction: Shapes.LiteralVector3) => {
runningEngine.applyGravity = true; runningEngine.applyGravity = true;
on stop runningEngine.applyGravity = false; 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 TAU = 2 * Math.PI;
const currentRotation = () => Shapes.Vector3(runningEngine.rotation);
field position: Shapes.Vector3 = currentPosition(); const currentPosition = () => u3(runningEngine.position);
field rotation: Shapes.Vector3 = currentRotation(); const currentRotation = () => u3(runningEngine.rotation, 1/TAU);
field position: Shapes.ImmediateVector3 = currentPosition();
field rotation: Shapes.ImmediateVector3 = currentRotation();
const refreshPeriod = Math.floor(1000 / 10); const refreshPeriod = Math.floor(1000 / 10);
at ds { at ds {
on message timer.PeriodicTick(refreshPeriod) => { on message timer.PeriodicTick(refreshPeriod) => {
const newPosition = currentPosition(); const newPosition = currentPosition();
const newRotation = currentRotation(); const newRotation = currentRotation();
if (!is(Shapes.fromVector3(position.value), Shapes.fromVector3(newPosition))) { if (!is(Shapes.fromImmediateVector3(position.value), Shapes.fromImmediateVector3(newPosition))) {
position.value = 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; rotation.value = newRotation;
} }
} }
@ -128,7 +131,7 @@ async function enterScene(
on asserted SceneProtocol.Portal({ on asserted SceneProtocol.Portal({
"name": o, "name": o,
"destination": $dest: SceneProtocol.PortalDestination, "destination": $dest: SceneProtocol.PortalDestination,
"position": $targetPosition: Shapes.Vector3, "position": $targetPosition: Shapes.LiteralVector3,
}) => { }) => {
const newPos = new Vector3(targetPosition.x, const newPos = new Vector3(targetPosition.x,
targetPosition.y, 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, name: id,
formals: [_POS, _HEAD, _BODY],
shape: B.nonphysical( shape: B.nonphysical(
B.move(position.value, B.many([ B.move(Shapes.Vector3.reference(_POS), B.many([
B.move({ x: 0, y: -0.9, z: 0 }, B.move(u3v({x: 0, y: -0.9, z: 0}),
B.rotate(scale3({ x: 0, y: rotation.value.y, z: 0 }, 1 / (2 * Math.PI)), B.rotate(Shapes.Vector3.reference(_BODY),
B.scale({ x: 0.4, y: 1.4, z: 0.1 }, B.box()))), B.scale(u3v({x: 0.4, y: 1.4, z: 0.1}), B.box()))),
B.rotate(scale3(rotation.value, 1 / (2 * Math.PI)), B.rotate(Shapes.Vector3.reference(_HEAD),
B.scale({ x: 0.15, y: 0.23, z: 0.18 }, B.many([ B.scale(u3v({x: 0.15, y: 0.23, z: 0.18}), B.many([
B.box(), B.box(),
B.move({ x: 0, y: 0, z: 0.501 }, B.move(u3v({x: 0, y: 0, z: 0.501}),
B.texture( B.texture(
Shapes.TextureSpec.uvAlpha({ Shapes.TextureSpec.uvAlpha({
path: `https://www.gravatar.com/avatar/${md5(new TextEncoder().encode(email.value.trim()))}?s=256&d=wavatar`, path: `https://www.gravatar.com/avatar/${md5(new TextEncoder().encode(email.value.trim()))}?s=256&d=wavatar`,
scale: Shapes.Vector2({ x:1, y:1 }), scale: Shapes.Vector2.immediate(u2({ x:1, y:1 })),
offset: Shapes.Vector2({ x:0, y:0 }), offset: Shapes.Vector2.immediate(u2({ x:0, y:0 })),
alpha: 1 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()))), B.plane()))),
]))), ]))),
]))), ]))),
}); });
console.log(s);
return s;
})();
} }
} }
@ -265,7 +285,7 @@ window.addEventListener('load', async () => {
Dataspace.boot(ds => { Dataspace.boot(ds => {
html.boot(ds); html.boot(ds);
timer.boot(ds); timer.boot(ds);
wsRelay.boot(ds, false); wsRelay.boot(ds, true);
wakeDetector.boot(ds); wakeDetector.boot(ds);
bootApp(ds, runningEngine); bootApp(ds, runningEngine);
}); });

View File

@ -20,10 +20,46 @@ import {
Vector2, Vector2,
Vector3, Vector3,
} from '@babylonjs/core/Legacy/legacy'; } 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'; 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 activeFloorMeshes: Array<AbstractMesh> = [];
export const activeTouchableMeshes: 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]); 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)) { if (is(Shapes.fromShape(shape), this.shapePreserve)) {
return this; return this;
} else { } else {
this.remove(); this.remove();
return build(name, this.scene, shape, { return build(env, name, this.scene, shape, {
spriteName: async m => m.rootnode.metadata.spriteName = spriteName, spriteName: async m => m.rootnode.metadata.spriteName = env.spriteName,
collisions: async m => (await m.allnodes).forEach(n => n.checkCollisions = true), 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 { export function dv(env: Environment, v: Shapes.DoubleValue, k: (v: number) => void) {
return new Vector2(v.x, v.y); 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 { export function u2(v: {x: number, y: number}): Shapes.ImmediateVector2 {
return new Vector3(v.x, v.y, v.z); 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 { export function lv2(env: Environment, v: Shapes.ImmediateVector2, k: (v: Vector2) => void) {
return Shapes.Vector3({ x: v.x * scale, y: v.y * scale, z: v.z * scale }); dv(env, v.x, x => dv(env, v.y, y => k(new Vector2(x, y))));
} }
export function q(q: Shapes.Quaternion): Quaternion { export function v2(env: Environment, v: Shapes.Vector2, k: (v: Vector2) => void) {
return new Quaternion(q.a, q.b, q.c, q.d); 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) }; export type MeshCustomizer = { [key: string]: ((m: ShapeTree<AbstractMesh>) => void) };
@ -111,7 +191,7 @@ type CachedTexture = {
}; };
const textureCache = new KeyedDictionary<Value, 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 cacheKey = Shapes.fromTextureSpec(spec);
const entry = textureCache.get(cacheKey); const entry = textureCache.get(cacheKey);
if (entry !== void 0) { if (entry !== void 0) {
@ -125,18 +205,18 @@ function buildTexture(name: string, scene: Scene, spec: Shapes.TextureSpec): Cac
case "simple": case "simple":
break; break;
case "uvAlpha": case "uvAlpha":
mat.alpha = spec.alpha; mat.alpha = 0;
dv(env, spec.alpha, a => mat.alpha = a);
tex.hasAlpha = true; tex.hasAlpha = true;
/* FALL THROUGH */ /* FALL THROUGH */
case "uv": { case "uv":
const scale = v2(spec.scale); v2(env, spec.scale, scale => v2(env, spec.offset, offset => {
const offset = v2(spec.offset); tex.uScale = scale.x;
tex.uScale = scale.x; tex.vScale = scale.y;
tex.vScale = scale.y; tex.uOffset = offset.x;
tex.uOffset = offset.x; tex.vOffset = offset.y;
tex.vOffset = offset.y; }));
break; break;
}
} }
const newEntry = { const newEntry = {
key: cacheKey, key: cacheKey,
@ -167,13 +247,7 @@ export function buildMesh(
switch (meshSpec._variant) { switch (meshSpec._variant) {
case "Sphere": return { rootnode: MeshBuilder.CreateSphere(name, {}, scene) }; case "Sphere": return { rootnode: MeshBuilder.CreateSphere(name, {}, scene) };
case "Box": return { rootnode: MeshBuilder.CreateBox(name, {}, scene) }; case "Box": return { rootnode: MeshBuilder.CreateBox(name, {}, scene) };
case "Ground": { case "Ground": return { rootnode: MeshBuilder.CreateGround(name, {}, scene ?? void 0) };
const v = v2(meshSpec.value.size);
return {
rootnode: MeshBuilder.CreateGround(
name, { width: v.x, height: v.y }, scene ?? void 0),
};
}
case "Plane": return { rootnode: MeshBuilder.CreatePlane(name, {}, scene) }; case "Plane": return { rootnode: MeshBuilder.CreatePlane(name, {}, scene) };
case "External": { case "External": {
const rootnode = new Mesh(name, scene); 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); 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) { switch (shape._variant) {
case "Mesh": { case "Mesh": {
const m = buildMesh(name, scene, shape.value); const m = buildMesh(name, scene, shape.value);
@ -219,23 +293,23 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
return t; return t;
} }
case "Light": case "Light": {
return new ShapeTree( const light = new HemisphericLight(name, new Vector3(0, 1, 0), scene);
scene, v3(env, shape.value.v, v => light.direction = v);
shape, return new ShapeTree(scene, shape, light);
new HemisphericLight(name, v3(shape.value.v), scene)); }
case "Scale": { case "Scale": {
const t = ShapeTree.transform(name, scene, shape); const t = ShapeTree.transform(name, scene, shape);
t.rootnode.scaling = v3(shape.value.v); v3(env, shape.value.v, v => t.rootnode.scaling = v);
build(name + '.inner', scene, shape.value.shape, customize).parent = t; build(env, name + '.inner', scene, shape.value.shape, customize).parent = t;
return t; return t;
} }
case "Move": { case "Move": {
const t = ShapeTree.transform(name, scene, shape); const t = ShapeTree.transform(name, scene, shape);
t.rootnode.position = v3(shape.value.v); v3(env, shape.value.v, v => t.rootnode.position = v);
build(name + '.inner', scene, shape.value.shape, customize).parent = t; build(env, name + '.inner', scene, shape.value.shape, customize).parent = t;
return 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); const t = ShapeTree.transform(name, scene, shape);
switch (shape.value._variant) { switch (shape.value._variant) {
case "euler": case "euler":
t.rootnode.rotation = v3(shape.value.v); v3(env, shape.value.v, v => {
t.rootnode.rotation.scaleInPlace(2 * Math.PI); t.rootnode.rotation = v;
t.rootnode.rotation.scaleInPlace(2 * Math.PI);
});
break; break;
case "quaternion": case "quaternion":
t.rootnode.rotationQuaternion = q(shape.value.q); q(env, shape.value.q, q => t.rootnode.rotationQuaternion = q);
break; break;
} }
build(name + '.inner', scene, shape.value.shape, customize).parent = t; build(env, name + '.inner', scene, shape.value.shape, customize).parent = t;
return t; return t;
} }
case "many": { case "many": {
const t = ShapeTree.transform(name, scene, shape); const t = ShapeTree.transform(name, scene, shape);
shape.value.forEach((s, i) => { shape.value.forEach((s, i) => {
build(name + '[' + i + ']', scene, s, customize).parent = t; build(env, name + '[' + i + ']', scene, s, customize).parent = t;
}); });
return t; return t;
} }
case "Texture": { case "Texture": {
const entry = buildTexture(name + '.texture', scene, shape.value.spec); const entry = buildTexture(env, name + '.texture', scene, shape.value.spec);
const t = build(name + '.inner', scene, shape.value.shape, { const t = build(env, name + '.inner', scene, shape.value.shape, {
... customize, ... customize,
material: async m => (await m.allnodes).forEach(n => n.material = entry.material), 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": { case "Color": {
const mat = new StandardMaterial(name + '.texture', scene); const mat = new StandardMaterial(name + '.texture', scene);
mat.diffuseColor = new Color3(shape.value.r, shape.value.g, shape.value.b); dv(env, shape.value.r, r =>
if (shape.value._variant === "transparent") mat.alpha = shape.value.alpha; dv(env, shape.value.g, g =>
const t = build(name + '.inner', scene, shape.value.shape, { 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, ... customize,
material: async m => (await m.allnodes).forEach(n => n.material = mat), 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": { case "Sound": {
const sound = buildSound(name + ".sound", scene, shape.value.spec, true); 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, ... customize,
sound: async m => sound.attachToMesh(m.rootnode), sound: async m => sound.attachToMesh(m.rootnode),
}); });
@ -295,10 +375,10 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
} }
case "Name": 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": case "Floor":
return build(name, scene, shape.value.shape, { return build(env, name, scene, shape.value.shape, {
... customize, ... customize,
floor: async m => { floor: async m => {
const nodes = await m.allnodes; const nodes = await m.allnodes;
@ -311,13 +391,13 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize
}); });
case "Nonphysical": case "Nonphysical":
return build(name, scene, shape.value.shape, { return build(env, name, scene, shape.value.shape, {
... customize, ... customize,
collisions: async m => (await m.allnodes).forEach(n => n.checkCollisions = false), collisions: async m => (await m.allnodes).forEach(n => n.checkCollisions = false),
}); });
case "Touchable": case "Touchable":
return build(name, scene, shape.value.shape, { return build(env, name, scene, shape.value.shape, {
... customize, ... customize,
touchable: async m => { touchable: async m => {
const nodes = await m.allnodes; 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())), sphere: () => Shapes.Shape.Mesh(Shapes.Mesh.Sphere(Shapes.Sphere())),
box: () => Shapes.Shape.Mesh(Shapes.Mesh.Box(Shapes.Box())), box: () => Shapes.Shape.Mesh(Shapes.Mesh.Box(Shapes.Box())),
plane: () => Shapes.Shape.Mesh(Shapes.Mesh.Plane(Shapes.Plane())), plane: () => Shapes.Shape.Mesh(Shapes.Mesh.Plane(Shapes.Plane())),
ground: (v: Shapes.Vector2) => Shapes.Shape.Mesh(Shapes.Mesh.Ground(Shapes.Ground(Shapes.Vector2(v)))), ground: () => Shapes.Shape.Mesh(Shapes.Mesh.Ground(Shapes.Ground())),
light: (v: Shapes.Vector3) => Shapes.Shape.Light(Shapes.Light(Shapes.Vector3(v))), light: (v: Shapes.Vector3) => Shapes.Shape.Light(Shapes.Light(v)),
scale: (v: Shapes.Vector3, shape: Shapes.Shape) => Shapes.Shape.Scale(Shapes.Scale({ v: Shapes.Vector3(v), shape })), 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: Shapes.Vector3(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: Shapes.Vector3(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), many: (shapes: Shapes.Shape[]) => Shapes.Shape.many(shapes),
texture: (spec: Shapes.TextureSpec, shape: Shapes.Shape) => Shapes.Shape.Texture(Shapes.Texture({ spec, shape })), 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) => { color: (r0: number | symbol, g0: number | symbol, b0: number | symbol, shape: Shapes.Shape, alpha0: number | symbol = 1.0) => {
return Shapes.Shape.Color((alpha === 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.opaque({ r, g, b, shape })
: Shapes.Color.transparent({ r, g, b, alpha, shape })); : Shapes.Color.transparent({ r, g, b, alpha, shape }));
}, },
name: (base: string, shape: Shapes.Shape) => Shapes.Shape.Name(Shapes.Name({ base, shape })), name: (base: string, shape: Shapes.Shape) => Shapes.Shape.Name(Shapes.Name({ base, shape })),
floor: (shape: Shapes.Shape) => Shapes.Shape.Floor(Shapes.Floor(shape)), floor: (shape: Shapes.Shape) => Shapes.Shape.Floor(Shapes.Floor(shape)),
nonphysical: (shape: Shapes.Shape) => Shapes.Shape.Nonphysical(Shapes.Nonphysical(shape)), nonphysical: (shape: Shapes.Shape) => Shapes.Shape.Nonphysical(Shapes.Nonphysical(shape)),
}; } satisfies { [key: string]: (... args: any[]) => Shapes.Shape };