From bfbc2fdf34654cc7f90e656a412a5b5eea807235 Mon Sep 17 00:00:00 2001 From: Paulo Feodrippe Date: Wed, 29 Jan 2025 00:37:16 -0500 Subject: [PATCH] [vc] Move animation-node-player to VybeC --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 1 + src/vybe/c.clj | 30 +++++++--- src/vybe/flecs.clj | 7 +++ src/vybe/game/system.clj | 123 ++++++++++++++++++++------------------- test/vybe/flecs_test.clj | 4 +- todo.md | 33 ++++++----- 7 files changed, 114 insertions(+), 86 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07ac5ece..de893ea8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: branches: - main - # - develop + - develop paths-ignore: - '**/README.md' - '**/CHANGELOG.md' diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b0c14d7..ca6a644f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Now it's working fine with the `vybe-games` builds - Create `vc/eval*` to debug standalone forms - Accept meta `^:void` in `if`/`cond` expressions to signal to the C compiler that we don't care about the return + - Move animation-node-player to VybeC - Add `vf/defsystem-c` ## v0.7.469 diff --git a/src/vybe/c.clj b/src/vybe/c.clj index 914c52bc..3368dc83 100644 --- a/src/vybe/c.clj +++ b/src/vybe/c.clj @@ -263,7 +263,7 @@ (let [{:keys [ret args]} (vp/fn-descriptor->map type)] (format "%s (*%s)(%s);" (-adapt-type (:schema ret)) - (name k) + (->name k) (->> (mapv (fn [{:keys [symbol schema]}] (str (-adapt-type schema) " " (->name symbol))) args) @@ -277,7 +277,7 @@ (comp->c vec-type (merge (update opts :level inc) {:embedded true})) (-adapt-type vec-type)) - (name k) + (->name k) size)) :else @@ -311,7 +311,7 @@ :type type :component component} e))))) - " " (name k) ";"))))) + " " (->name k) ";"))))) (mapv #(str nesting %)) (str/join "\n")) (if embedded @@ -320,6 +320,7 @@ (if embedded "" (str " " c-name ";")))))) +#_ (println (comp->c vybe.jolt/VyBody)) #_ (println (comp->c vybe.flecs/system_desc_t)) #_ (println (comp->c VybeAllocator)) #_ (println (comp->c Unit)) @@ -1203,9 +1204,11 @@ signal(SIGSEGV, sighandler); (tap> {:label "Error when transpiling to C" :error error-map}) - (let [{:keys [file line column]} (:metadata (ex-data e))] - (throw (clojure.lang.Compiler$CompilerException. file line column e)) - #_(throw (ex-info "Error when transpiling to C" error-map))))))) + (try + (let [{:keys [file line column]} (:metadata (ex-data e))] + (throw (clojure.lang.Compiler$CompilerException. file line column e))) + (catch Exception _e + (throw (ex-info "Error when transpiling to C" error-map)))))))) (defn- adapt-schema [schema] @@ -1463,7 +1466,7 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i ([code-form {:keys [sym-meta sym sym-name] :as opts}] (let [{:keys [c-code ::c-data form-hash final-form init-struct-val]} (-> code-form - (transpile (assoc opts ::version 52))) + (transpile (assoc opts ::version 53))) obj-name (str "vybe_" sym-name "_" (when (or (:no-cache sym-meta) @@ -1991,6 +1994,19 @@ long long int: \"long long int\", unsigned long long int: \"unsigned long long i [{:keys [form]}] (eval form)) +(defn cast* + "Cast a struct into another. Does nothing in the JVM." + [v c] + v) + +(defmethod c-macroexpand #'cast* + [{:keys [args]}] + `(let [v# ~(first args)] + (-> v# + vp/& + (vp/as [:* ~(second args)]) + deref))) + (declare ^:no-ns typeof ^:no-ns typename #_^:no-ns comptime) diff --git a/src/vybe/flecs.clj b/src/vybe/flecs.clj index ff173f32..48bc136b 100644 --- a/src/vybe/flecs.clj +++ b/src/vybe/flecs.clj @@ -2346,9 +2346,16 @@ (if (map? binding-form) ;; Map destructuring. (vec (concat [res-internal-sym form] + ;; :keys destructuring (->> (:keys binding-form) (mapcat #(vector % (list (keyword %) + `(deref + ~res-internal-sym))))) + ;; {aabb-min :min aabb-max :max} destructuring + (->> (dissoc binding-form :keys) + (mapcat #(vector (first %) + (list (keyword (second %)) `(deref ~res-internal-sym))))))) ;; No destructuring. diff --git a/src/vybe/game/system.clj b/src/vybe/game/system.clj index d16390d4..e3f9b7ab 100644 --- a/src/vybe/game/system.clj +++ b/src/vybe/game/system.clj @@ -212,45 +212,45 @@ t)) (keys p1)))))) -(vf/defsystem animation-node-player w - [[_ node] [:vg.anim/target-node :*] - _ [:vg.anim/target-component '?c] - {:keys [id]} [:src '?c vf/VybeComponentId] - node-ref vf/Ref - {:keys [timeline_count values timeline]} vt/AnimationChannel - player [:meta {:flags #{:up :cascade} - :inout :mut} - vt/AnimationPlayer] - parent-e [:vf/entity {:flags #{:up}} :vg.anim/active] - _ [:not {:flags #{:up}} :vg.anim/stop]] - #_(println :c c) - - #_(def aaa ) - (let [c (vp/comp-cache id) - values (vp/arr values timeline_count c) - timeline (vp/arr timeline timeline_count :float) - idx* (first (indices #(>= % (:current_time player)) timeline)) - idx (max (dec (or idx* (count timeline))) 0) - t (when idx* - (/ (- (:current_time player) - (nth timeline idx)) - (- (nth timeline (inc idx)) - (nth timeline idx))))] - - (when-not idx* - (conj parent-e :vg.anim/stop) - ;; Just for triggering the `animation-loop` system. - (conj (vf/ent w node) :vg.anim.entity/stop)) - - ;; We modify the component from the ref and then we have to notify flecs - ;; that it was modified. - (merge @node-ref (if t - (lerp-p (nth values idx) - (nth values (inc idx)) - t) - (nth values idx))) - - (vf/modified! w node c))) +#_(vf/defsystem animation-node-player w + [[_ node] [:vg.anim/target-node :*] + _ [:vg.anim/target-component '?c] + {:keys [id]} [:src '?c vf/VybeComponentId] + node-ref vf/Ref + {:keys [timeline_count values timeline]} vt/AnimationChannel + player [:meta {:flags #{:up :cascade} + :inout :mut} + vt/AnimationPlayer] + parent-e [:vf/entity {:flags #{:up}} :vg.anim/active] + _ [:not {:flags #{:up}} :vg.anim/stop]] + #_(println :c c) + + #_(def aaa ) + (let [c (vp/comp-cache id) + values (vp/arr values timeline_count c) + timeline (vp/arr timeline timeline_count :float) + idx* (first (indices #(>= % (:current_time player)) timeline)) + idx (max (dec (or idx* (count timeline))) 0) + t (when idx* + (/ (- (:current_time player) + (nth timeline idx)) + (- (nth timeline (inc idx)) + (nth timeline idx))))] + + (when-not idx* + (conj parent-e :vg.anim/stop) + ;; Just for triggering the `animation-loop` system. + (conj (vf/ent w node) :vg.anim.entity/stop)) + + ;; We modify the component from the ref and then we have to notify flecs + ;; that it was modified. + (merge @node-ref (if t + (lerp-p (nth values idx) + (nth values (inc idx)) + t) + (nth values idx))) + + (vf/modified! w node c))) ;; TODO We could also c-macroexpand/c-invoke from a protocol. (defmethod vc/c-macroexpand #'conj @@ -264,11 +264,13 @@ c))) nil)) -(vf/defsystem-c animation-node-player-2 w +(vf/defsystem-c animation-node-player w [[_ node] [:vg.anim/target-node '?node] + ;; TODO We could have some sugar to get multiple components from the same entity. translation [:src '?node vt/Translation] scale [:src '?node vt/Scale] rotation [:src '?node vt/Rotation] + [_ c] [:vg.anim/target-component '?c] {:keys [kind timeline_count values timeline]} vt/AnimationChannel player [:meta {:flags #{:up :cascade} @@ -280,14 +282,9 @@ ;; TODO OPTM We could also leverage the previous index. idx* (vc/bs_lower_bound timeline* timeline_count (:current_time @player)) idx (cond - (= idx* 0) - 0 - - (>= idx* timeline_count) - -1 - - :else - (dec idx*))] + (= idx* 0) 0 + (>= idx* timeline_count) -1 + :else (dec idx*))] (if (>= idx 0) (let [v (/ (- (:current_time @player) @@ -299,22 +296,28 @@ ^:void (cond (= kind 0) - (merge @translation - (nth (vp/arr values timeline_count vt/Translation) idx)) + (let [arr (vp/arr values timeline_count vt/Translation)] + (merge @translation + (-> (vr.c/vector-3-lerp (-> (nth arr idx) (vc/cast* vt/Vector3)) + (-> (nth arr (inc idx)) (vc/cast* vt/Vector3)) + v) + (vc/cast* vt/Translation)))) (= kind 1) - (merge @scale - (nth (vp/arr values timeline_count vt/Scale) idx)) + (let [arr (vp/arr values timeline_count vt/Scale)] + (merge @scale + (-> (vr.c/vector-3-lerp (-> (nth arr idx) (vc/cast* vt/Vector3)) + (-> (nth arr (inc idx)) (vc/cast* vt/Vector3)) + v) + (vc/cast* vt/Scale)))) (= kind 2) - (merge @rotation - (nth (vp/arr values timeline_count vt/Rotation) idx))) - - #_(merge @node-ref (if t - (lerp-p (nth values idx) - (nth values (inc idx)) - t) - (nth values idx))) + (let [arr (vp/arr values timeline_count vt/Rotation)] + (merge @rotation + (-> (vr.c/quaternion-slerp (-> (nth arr idx) (vc/cast* vt/Vector4)) + (-> (nth arr (inc idx)) (vc/cast* vt/Vector4)) + v) + (vc/cast* vt/Rotation))))) ;; We modify the component from the ref and then we have to notify flecs ;; that it was modified. diff --git a/test/vybe/flecs_test.clj b/test/vybe/flecs_test.clj index 130527ee..fdb5e77d 100644 --- a/test/vybe/flecs_test.clj +++ b/test/vybe/flecs_test.clj @@ -97,8 +97,8 @@ (vg.s/vybe-transform w) (testing "system builders can be called over and over if they don't change" - (= (vf/eid (vg.s/animation-node-player-2 w)) - (vf/eid (vg.s/animation-node-player-2 w)))) + (= (vf/eid (vg.s/animation-node-player w)) + (vf/eid (vg.s/animation-node-player w)))) (merge w {:alice [(vt/Scale [1.0 1.0 1.0]) (vt/Translation) (vt/Rotation [0 0 0 1]) [(vt/Transform) :global] (vt/Transform) diff --git a/todo.md b/todo.md index 6eb0afea..f43cced3 100644 --- a/todo.md +++ b/todo.md @@ -272,7 +272,7 @@ - [x] macro to quickly evaluate stuff - [-] REPL plugin like portal does for cljs? - [x] https://github.com/pfeodrippe/vybe/issues/4 -- [ ] try to VybeC animation-node-player +- [x] try to VybeC animation-node-player - [x] don't use `-field` for non components - [x] make vector destructuring work - [x] get entity corresponding to the position @@ -282,7 +282,7 @@ - [x] don't bind `_.*` - [x] scoped :vf/entity - [x] tap - - [ ] body of the orignal CLJ function + - [x] body of the orignal CLJ function - [x] accept 3-arity version of `vp/arr` - [x] do we have a way to get the component dynamically? - lerp later @@ -308,20 +308,12 @@ - [-] create cond automatically from it? - [x] fix `idx` - [x] fix `t` - - [ ] lerp - - [ ] just idx for translation - - [ ] scale - - [ ] rotation - - [ ] use lerp - - [ ] :vf/entity should return a struct? - - [ ] consider making systems `always` by default - - [ ] `def` to a var from C? - - [x] show line/column error correctly for an error before calling the compiler - - [ ] Remove variables starting with a `_` in a `let` (just do the side-effect) - - [ ] optimization (-O3)? - - [ ] how to represent `nil` properly? - - [ ] maybe with nullable - - [ ] should have slices? (support for `count`, `first`, `last`, `nth`, iteration) + - [x] lerp + - [x] just idx for translation + - [x] scale + - [x] rotation + - [x] use lerp +- [ ] move `update-physics` system - [ ] built-in models - [x] minimal - [ ] more complex @@ -450,6 +442,7 @@ - [ ] flecs system - [ ] tap from C into portal - [ ] VybeC + - [x] show line/column error correctly for an error before calling the compiler - [ ] better way to jump to definition - [ ] allocator setting - [ ] comptime @@ -461,6 +454,14 @@ - [ ] make `@, vp/&` etc better - [ ] create a merge or reset! "protocol" leveraging C generics - [ ] make recompilation work with an indirection? + - [ ] :vf/entity should return a struct? + - [ ] consider making systems `always` by default + - [ ] `def` to a var from C? + - [ ] Remove variables starting with a `_` in a `let` (just do the side-effect) + - [ ] optimization (-O3)? + - [ ] how to represent `nil` properly? + - [ ] maybe with nullable + - [ ] should have slices? (support for `count`, `first`, `last`, `nth`, iteration) - [ ] make it easy to DSP in the frequency domain - [ ] create filters in realtime from clerk - [ ] wasm