@lovelace_lol/polymer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/dist/index.d.ts +2603 -0
- package/dist/index.js +1031 -0
- package/dist/test.cjs +1519 -0
- package/dist/worker/agency-worker.js +972 -0
- package/dist/worker/manifest.edn +1 -0
- package/package.json +57 -0
- package/shadow-cljs.edn +173 -0
- package/src/polymer/animation/agency.cljs +84 -0
- package/src/polymer/animation/domain.cljs +110 -0
- package/src/polymer/animation/planner.cljs +88 -0
- package/src/polymer/animation/runtime.cljs +137 -0
- package/src/polymer/animation/scheduler.cljs +178 -0
- package/src/polymer/animation/service.cljs +981 -0
- package/src/polymer/animation/state.cljs +112 -0
- package/src/polymer/blink/agency.cljs +131 -0
- package/src/polymer/blink/planner.cljs +115 -0
- package/src/polymer/blink/scheduler.cljs +90 -0
- package/src/polymer/blink/snippet.cljs +50 -0
- package/src/polymer/blink/state.cljs +102 -0
- package/src/polymer/camera_context/agency.cljs +132 -0
- package/src/polymer/camera_context/domain.cljs +186 -0
- package/src/polymer/camera_context/planner.cljs +50 -0
- package/src/polymer/camera_context/scheduler.cljs +113 -0
- package/src/polymer/camera_context/state.cljs +121 -0
- package/src/polymer/character.cljs +471 -0
- package/src/polymer/conversation/agency.cljs +83 -0
- package/src/polymer/conversation/domain.cljs +35 -0
- package/src/polymer/conversation/effector.cljs +130 -0
- package/src/polymer/conversation/planner.cljs +178 -0
- package/src/polymer/conversation/scheduler.cljs +202 -0
- package/src/polymer/conversation/service.cljs +329 -0
- package/src/polymer/conversation/state.cljs +186 -0
- package/src/polymer/core.cljs +172 -0
- package/src/polymer/eye_head/agency.cljs +75 -0
- package/src/polymer/eye_head/domain.cljs +133 -0
- package/src/polymer/eye_head/goap.cljs +106 -0
- package/src/polymer/eye_head/scheduler.cljs +176 -0
- package/src/polymer/eye_head/service.cljs +259 -0
- package/src/polymer/eye_head/snippet.cljs +92 -0
- package/src/polymer/eye_head/state.cljs +167 -0
- package/src/polymer/gaze/agency.cljs +85 -0
- package/src/polymer/gaze/domain.cljs +211 -0
- package/src/polymer/gaze/goap.cljs +208 -0
- package/src/polymer/gaze/scheduler.cljs +184 -0
- package/src/polymer/gaze/state.cljs +202 -0
- package/src/polymer/gesture/agency.cljs +185 -0
- package/src/polymer/gesture/goap.cljs +461 -0
- package/src/polymer/gesture/scheduler.cljs +122 -0
- package/src/polymer/gesture/snippet.cljs +198 -0
- package/src/polymer/gesture/state.cljs +273 -0
- package/src/polymer/lipsync/agency.cljs +593 -0
- package/src/polymer/lipsync/articulation/jaw.cljs +258 -0
- package/src/polymer/lipsync/articulation/snippet.cljs +605 -0
- package/src/polymer/lipsync/articulation/tongue.cljs +418 -0
- package/src/polymer/lipsync/articulation/visemes.cljs +429 -0
- package/src/polymer/lipsync/goap.cljs +132 -0
- package/src/polymer/lipsync/pronunciation/effector.cljs +51 -0
- package/src/polymer/lipsync/scheduler.cljs +302 -0
- package/src/polymer/lipsync/state.cljs +202 -0
- package/src/polymer/nlp/phonemes.cljs +164 -0
- package/src/polymer/prosodic/agency.cljs +196 -0
- package/src/polymer/prosodic/goap.cljs +122 -0
- package/src/polymer/prosodic/scheduler.cljs +99 -0
- package/src/polymer/prosodic/snippet.cljs +106 -0
- package/src/polymer/prosodic/state.cljs +139 -0
- package/src/polymer/runtime/embody.cljs +117 -0
- package/src/polymer/runtime/embody_wasm.cljs +177 -0
- package/src/polymer/runtime/rust_embody_host.cljs +645 -0
- package/src/polymer/society.cljs +239 -0
- package/src/polymer/stream.cljs +130 -0
- package/src/polymer/transcription/agency.cljs +69 -0
- package/src/polymer/transcription/domain.cljs +68 -0
- package/src/polymer/transcription/effector.cljs +611 -0
- package/src/polymer/transcription/planner.cljs +203 -0
- package/src/polymer/transcription/scheduler.cljs +225 -0
- package/src/polymer/transcription/service.cljs +209 -0
- package/src/polymer/transcription/state.cljs +239 -0
- package/src/polymer/tts/agency.cljs +503 -0
- package/src/polymer/tts/azure.cljs +290 -0
- package/src/polymer/tts/planner.cljs +178 -0
- package/src/polymer/tts/runtime.cljs +624 -0
- package/src/polymer/tts/scheduler.cljs +162 -0
- package/src/polymer/tts/state.cljs +191 -0
- package/src/polymer/worker.cljs +70 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
(ns polymer.runtime.rust-embody-host
|
|
2
|
+
"Polymer-owned CLJS host for Embody's Rust/Wasm RuntimeCore.
|
|
3
|
+
|
|
4
|
+
Rust owns profile/preset intake, binding compilation, control state, and
|
|
5
|
+
packed frame evaluation. This namespace owns the JavaScript object exposed to
|
|
6
|
+
hosts and the narrow Three.js inspection/application boundary."
|
|
7
|
+
(:require ["@lovelace_lol/embody" :as embody]
|
|
8
|
+
[polymer.runtime.embody-wasm :as embody-wasm]))
|
|
9
|
+
|
|
10
|
+
(def ThreeModelInspector (aget embody "ThreeModelInspector"))
|
|
11
|
+
(def ThreeFrameApplier (aget embody "ThreeFrameApplier"))
|
|
12
|
+
(def ThreeAnimationRuntime (aget embody "ThreeAnimationRuntime"))
|
|
13
|
+
(def ThreeAnimationSystem (aget embody "ThreeAnimationSystem"))
|
|
14
|
+
(def HairPhysicsController (aget embody "HairPhysicsController"))
|
|
15
|
+
(def build-frame-applier-bindings (aget embody "buildFrameApplierBindings"))
|
|
16
|
+
(def get-mesh-names-for-au-profile (aget embody "getMeshNamesForAUProfile"))
|
|
17
|
+
(def get-mesh-names-for-viseme-profile (aget embody "getMeshNamesForVisemeProfile"))
|
|
18
|
+
|
|
19
|
+
(defn- js-call
|
|
20
|
+
[target method & args]
|
|
21
|
+
(let [f (when target (aget target method))]
|
|
22
|
+
(when-not (fn? f)
|
|
23
|
+
(throw (js/Error. (str "Character host is missing method: " method))))
|
|
24
|
+
(.apply f target (to-array args))))
|
|
25
|
+
|
|
26
|
+
(defn- js-new
|
|
27
|
+
[ctor & args]
|
|
28
|
+
(when-not ctor
|
|
29
|
+
(throw (js/Error. "The installed Embody package is missing a required Three.js adapter export.")))
|
|
30
|
+
(.construct js/Reflect ctor (to-array args)))
|
|
31
|
+
|
|
32
|
+
(defn- js-values
|
|
33
|
+
[value]
|
|
34
|
+
(if (array? value) (array-seq value) ()))
|
|
35
|
+
|
|
36
|
+
(defn- clamp01
|
|
37
|
+
[value]
|
|
38
|
+
(max 0 (min 1 (if (and (number? value) (js/isFinite value)) value 0))))
|
|
39
|
+
|
|
40
|
+
(defn- normalize-preset-id
|
|
41
|
+
[preset-id]
|
|
42
|
+
(let [normalized (when (string? preset-id) (.toLowerCase (.trim preset-id)))]
|
|
43
|
+
(case normalized
|
|
44
|
+
(nil "" "default" "human") "cc4"
|
|
45
|
+
normalized)))
|
|
46
|
+
|
|
47
|
+
(defn- normalize-profile-mode
|
|
48
|
+
[profile-mode]
|
|
49
|
+
(let [normalized (when (string? profile-mode)
|
|
50
|
+
(.toLowerCase (.trim profile-mode)))]
|
|
51
|
+
(case normalized
|
|
52
|
+
(nil "" "preset" "embedded-preset") "embedded-preset"
|
|
53
|
+
("exact" "exact-profile") "exact-profile"
|
|
54
|
+
(throw (js/Error.
|
|
55
|
+
(str "Unknown Rust Embody profileMode \"" profile-mode
|
|
56
|
+
"\". Expected \"embedded-preset\" or \"exact-profile\"."))))))
|
|
57
|
+
|
|
58
|
+
(defn- profile-json
|
|
59
|
+
[profile required?]
|
|
60
|
+
(cond
|
|
61
|
+
(nil? profile)
|
|
62
|
+
(if required?
|
|
63
|
+
(throw (js/Error. "Rust Embody exact-profile mode requires a profile object."))
|
|
64
|
+
"")
|
|
65
|
+
|
|
66
|
+
(or (array? profile) (not= "object" (goog/typeOf profile)))
|
|
67
|
+
(throw (js/Error. "Rust Embody profile must be a JSON object."))
|
|
68
|
+
|
|
69
|
+
:else
|
|
70
|
+
(.stringify js/JSON profile)))
|
|
71
|
+
|
|
72
|
+
(defn supports-preset?
|
|
73
|
+
"True when the initialized Rust/Wasm registry contains the requested preset."
|
|
74
|
+
[preset-id]
|
|
75
|
+
(let [wasm (embody-wasm/get-core)]
|
|
76
|
+
(boolean (js-call wasm "has_preset" (normalize-preset-id preset-id)))))
|
|
77
|
+
|
|
78
|
+
(def supportsRustEmbodyPreset supports-preset?)
|
|
79
|
+
|
|
80
|
+
(defn- profile-map
|
|
81
|
+
[profile key]
|
|
82
|
+
(or (when profile (aget profile key)) #js {}))
|
|
83
|
+
|
|
84
|
+
(defn- profile-array
|
|
85
|
+
[profile key]
|
|
86
|
+
(let [value (when profile (aget profile key))]
|
|
87
|
+
(if (array? value) value #js [])))
|
|
88
|
+
|
|
89
|
+
(defn- resolved-meshes
|
|
90
|
+
[meshes mesh-by-name profile mesh-names]
|
|
91
|
+
(let [requested (if (and (array? mesh-names) (pos? (.-length mesh-names)))
|
|
92
|
+
mesh-names
|
|
93
|
+
(let [face (aget (profile-map profile "morphToMesh") "face")]
|
|
94
|
+
(if (and (array? face) (pos? (.-length face)))
|
|
95
|
+
face
|
|
96
|
+
(.map meshes (fn [mesh] (.-name mesh))))))]
|
|
97
|
+
(keep (fn [name] (.get mesh-by-name name)) (js-values requested))))
|
|
98
|
+
|
|
99
|
+
(defn- resolve-morph-targets
|
|
100
|
+
[meshes mesh-by-name profile morph-key mesh-names]
|
|
101
|
+
(reduce
|
|
102
|
+
(fn [targets mesh]
|
|
103
|
+
(let [dictionary (aget mesh "morphTargetDictionary")
|
|
104
|
+
influences (aget mesh "morphTargetInfluences")
|
|
105
|
+
index (cond
|
|
106
|
+
(number? morph-key) morph-key
|
|
107
|
+
dictionary (aget dictionary morph-key)
|
|
108
|
+
:else nil)]
|
|
109
|
+
(if (and influences (number? index) (<= 0 index) (< index (.-length influences)))
|
|
110
|
+
(conj targets #js {:infl influences :idx index})
|
|
111
|
+
targets)))
|
|
112
|
+
[]
|
|
113
|
+
(resolved-meshes meshes mesh-by-name profile mesh-names)))
|
|
114
|
+
|
|
115
|
+
(defn- current-morph-value
|
|
116
|
+
[meshes mesh-by-name profile morph-key mesh-names]
|
|
117
|
+
(reduce
|
|
118
|
+
(fn [current target]
|
|
119
|
+
(max current (or (aget (aget target "infl") (aget target "idx")) 0)))
|
|
120
|
+
0
|
|
121
|
+
(resolve-morph-targets meshes mesh-by-name profile morph-key mesh-names)))
|
|
122
|
+
|
|
123
|
+
(defn- mixed-au?
|
|
124
|
+
[profile au-id]
|
|
125
|
+
(let [morphs (aget (profile-map profile "auToMorphs") (str au-id))
|
|
126
|
+
bones (aget (profile-map profile "auToBones") (str au-id))
|
|
127
|
+
has-morphs (and morphs
|
|
128
|
+
(some #(pos? (.-length (or (aget morphs %) #js [])))
|
|
129
|
+
["left" "right" "center"]))]
|
|
130
|
+
(boolean (and has-morphs (array? bones) (pos? (.-length bones))))))
|
|
131
|
+
|
|
132
|
+
(defn- animation-method!
|
|
133
|
+
[host animation-system method]
|
|
134
|
+
(aset host method
|
|
135
|
+
(fn [& args]
|
|
136
|
+
(apply js-call @animation-system method args))))
|
|
137
|
+
|
|
138
|
+
(def animation-methods
|
|
139
|
+
["loadAnimationClips"
|
|
140
|
+
"getAnimationClips"
|
|
141
|
+
"removeAnimationClip"
|
|
142
|
+
"playAnimation"
|
|
143
|
+
"stopAnimation"
|
|
144
|
+
"stopAllAnimations"
|
|
145
|
+
"pauseAnimation"
|
|
146
|
+
"resumeAnimation"
|
|
147
|
+
"pauseAllAnimations"
|
|
148
|
+
"resumeAllAnimations"
|
|
149
|
+
"setAnimationSpeed"
|
|
150
|
+
"setAnimationIntensity"
|
|
151
|
+
"setAnimationLoopMode"
|
|
152
|
+
"setAnimationRepeatCount"
|
|
153
|
+
"setAnimationReverse"
|
|
154
|
+
"setAnimationBlendMode"
|
|
155
|
+
"seekAnimation"
|
|
156
|
+
"setAnimationTimeScale"
|
|
157
|
+
"getAnimationState"
|
|
158
|
+
"getPlayingAnimations"
|
|
159
|
+
"crossfadeTo"
|
|
160
|
+
"snippetToClip"
|
|
161
|
+
"playClip"
|
|
162
|
+
"playSnippet"
|
|
163
|
+
"typedSnippetToClip"
|
|
164
|
+
"playTypedSnippet"
|
|
165
|
+
"buildClip"
|
|
166
|
+
"buildTypedClip"
|
|
167
|
+
"updateClipParams"
|
|
168
|
+
"cleanupSnippet"
|
|
169
|
+
"supportsClipCurves"])
|
|
170
|
+
|
|
171
|
+
(defn create-rust-embody-host-unbound
|
|
172
|
+
"Create Polymer's synchronous CLJS host after initEmbodyCore has completed."
|
|
173
|
+
([]
|
|
174
|
+
(create-rust-embody-host-unbound #js {}))
|
|
175
|
+
([config]
|
|
176
|
+
(let [config (or config #js {})
|
|
177
|
+
wasm (embody-wasm/get-core)
|
|
178
|
+
profile-mode (normalize-profile-mode (aget config "profileMode"))
|
|
179
|
+
exact-profile? (= profile-mode "exact-profile")
|
|
180
|
+
preset-id (normalize-preset-id (aget config "presetType"))
|
|
181
|
+
_ (when (and (not exact-profile?)
|
|
182
|
+
(not (js-call wasm "has_preset" preset-id)))
|
|
183
|
+
(throw (js/Error.
|
|
184
|
+
(str "Rust Embody preset \"" preset-id
|
|
185
|
+
"\" is not embedded. Known presets: "
|
|
186
|
+
(.join (js-call wasm "list_presets") ", ") "."))))
|
|
187
|
+
profile-source-json (atom (profile-json (aget config "profile") exact-profile?))
|
|
188
|
+
profile (atom (if exact-profile?
|
|
189
|
+
(.parse js/JSON @profile-source-json)
|
|
190
|
+
(.parse js/JSON
|
|
191
|
+
(js-call wasm
|
|
192
|
+
"merge_embedded_preset"
|
|
193
|
+
preset-id
|
|
194
|
+
@profile-source-json))))
|
|
195
|
+
RuntimeCore (aget wasm "RuntimeCore")
|
|
196
|
+
core (js-new RuntimeCore 0)
|
|
197
|
+
inspector (js-new ThreeModelInspector)
|
|
198
|
+
applier (js-new ThreeFrameApplier)
|
|
199
|
+
morph-transitions (js-new ThreeAnimationRuntime)
|
|
200
|
+
model (atom nil)
|
|
201
|
+
meshes (atom #js [])
|
|
202
|
+
all-meshes (atom #js [])
|
|
203
|
+
mesh-by-name (atom (js/Map.))
|
|
204
|
+
bones (atom #js {})
|
|
205
|
+
inspection (atom nil)
|
|
206
|
+
animation-system (atom nil)
|
|
207
|
+
hair-runtime (atom nil)
|
|
208
|
+
viseme-values (atom #js [])
|
|
209
|
+
mix-weights (atom {})
|
|
210
|
+
running? (atom false)
|
|
211
|
+
raf-id (atom nil)
|
|
212
|
+
last-frame-ms (atom nil)
|
|
213
|
+
host (js-obj)]
|
|
214
|
+
(letfn [(apply-frame! []
|
|
215
|
+
(let [morph-frame (js-call core "evaluate_morph_frame_delta")
|
|
216
|
+
bone-frame (js-call core "evaluate_bone_frame_delta")]
|
|
217
|
+
(when (pos? (.-length morph-frame))
|
|
218
|
+
(js-call applier "applyPackedMorphFrameDelta" morph-frame))
|
|
219
|
+
(when (pos? (.-length bone-frame))
|
|
220
|
+
(js-call applier "applyPackedBoneFrameDelta" bone-frame)
|
|
221
|
+
(when @model
|
|
222
|
+
(js-call @model "updateMatrixWorld" true)))))
|
|
223
|
+
|
|
224
|
+
(bind-model! [mesh-overrides]
|
|
225
|
+
(when @model
|
|
226
|
+
(let [options (if mesh-overrides
|
|
227
|
+
#js {:profile @profile :meshes mesh-overrides}
|
|
228
|
+
#js {:profile @profile})
|
|
229
|
+
result (js-call inspector "inspectModel" @model options)
|
|
230
|
+
model-meshes (aget result "allMeshes")
|
|
231
|
+
runtime-meshes (let [morph-meshes (aget result "morphMeshes")]
|
|
232
|
+
(if (and morph-meshes (pos? (.-length morph-meshes)))
|
|
233
|
+
morph-meshes
|
|
234
|
+
model-meshes))]
|
|
235
|
+
(reset! inspection result)
|
|
236
|
+
(reset! meshes runtime-meshes)
|
|
237
|
+
(reset! all-meshes model-meshes)
|
|
238
|
+
(reset! mesh-by-name (aget result "meshByName"))
|
|
239
|
+
(reset! bones (aget result "bones"))
|
|
240
|
+
(aset host "meshes" runtime-meshes)
|
|
241
|
+
(aset host "meshByName" @mesh-by-name)
|
|
242
|
+
(aset host "bones" @bones)
|
|
243
|
+
(js-call applier "setBindings" (build-frame-applier-bindings result))
|
|
244
|
+
(js-call applier
|
|
245
|
+
"applyMeshMaterialConfigs"
|
|
246
|
+
@model
|
|
247
|
+
(profile-map @profile "meshes"))
|
|
248
|
+
(when @hair-runtime
|
|
249
|
+
(js-call @hair-runtime "registerHairObjects" (to-array (hair-meshes)))
|
|
250
|
+
(js-call @hair-runtime
|
|
251
|
+
"setHairPhysicsProfileConfig"
|
|
252
|
+
(aget @profile "hairPhysics")))
|
|
253
|
+
(let [descriptor-json (.stringify js/JSON (aget result "descriptor"))]
|
|
254
|
+
(if exact-profile?
|
|
255
|
+
(js-call core
|
|
256
|
+
"configure_exact_profile"
|
|
257
|
+
@profile-source-json
|
|
258
|
+
descriptor-json)
|
|
259
|
+
(js-call core
|
|
260
|
+
"configure_with_preset"
|
|
261
|
+
preset-id
|
|
262
|
+
@profile-source-json
|
|
263
|
+
descriptor-json)))
|
|
264
|
+
(apply-frame!))))
|
|
265
|
+
|
|
266
|
+
(set-profile! [next-profile]
|
|
267
|
+
(js-call core "clear")
|
|
268
|
+
(js-call morph-transitions "clearTransitions")
|
|
269
|
+
(apply-frame!)
|
|
270
|
+
(reset! viseme-values #js [])
|
|
271
|
+
(reset! mix-weights {})
|
|
272
|
+
(reset! profile-source-json (profile-json next-profile exact-profile?))
|
|
273
|
+
(reset! profile
|
|
274
|
+
(if exact-profile?
|
|
275
|
+
(.parse js/JSON @profile-source-json)
|
|
276
|
+
(.parse js/JSON
|
|
277
|
+
(js-call wasm
|
|
278
|
+
"merge_embedded_preset"
|
|
279
|
+
preset-id
|
|
280
|
+
@profile-source-json))))
|
|
281
|
+
(bind-model! @meshes))
|
|
282
|
+
|
|
283
|
+
(get-au-mix-weight [au-id]
|
|
284
|
+
(or (get @mix-weights au-id)
|
|
285
|
+
(aget (profile-map @profile "auMixDefaults") (str au-id))
|
|
286
|
+
1))
|
|
287
|
+
|
|
288
|
+
(set-morph! [morph-key value mesh-names]
|
|
289
|
+
(let [targets (resolve-morph-targets
|
|
290
|
+
@meshes @mesh-by-name @profile morph-key mesh-names)
|
|
291
|
+
normalized (clamp01 value)]
|
|
292
|
+
(doseq [target targets]
|
|
293
|
+
(aset (aget target "infl") (aget target "idx") normalized))))
|
|
294
|
+
|
|
295
|
+
(transition-morph! [morph-key value duration-ms mesh-names]
|
|
296
|
+
(let [targets (resolve-morph-targets
|
|
297
|
+
@meshes @mesh-by-name @profile morph-key mesh-names)
|
|
298
|
+
from (reduce
|
|
299
|
+
(fn [current target]
|
|
300
|
+
(max current
|
|
301
|
+
(or (aget (aget target "infl") (aget target "idx")) 0)))
|
|
302
|
+
0
|
|
303
|
+
targets)
|
|
304
|
+
target (clamp01 value)
|
|
305
|
+
mesh-key (if (and (array? mesh-names) (pos? (.-length mesh-names)))
|
|
306
|
+
(.join mesh-names ",")
|
|
307
|
+
"*")]
|
|
308
|
+
(js-call morph-transitions
|
|
309
|
+
"addTransition"
|
|
310
|
+
(str "morph:" mesh-key ":" morph-key)
|
|
311
|
+
from
|
|
312
|
+
target
|
|
313
|
+
(or duration-ms 120)
|
|
314
|
+
(fn [next-value]
|
|
315
|
+
(doseq [resolved targets]
|
|
316
|
+
(aset (aget resolved "infl")
|
|
317
|
+
(aget resolved "idx")
|
|
318
|
+
(clamp01 next-value)))))))
|
|
319
|
+
|
|
320
|
+
(update! [dt-seconds]
|
|
321
|
+
(let [dt (max 0 (if (and (number? dt-seconds) (js/isFinite dt-seconds))
|
|
322
|
+
dt-seconds
|
|
323
|
+
0))]
|
|
324
|
+
(when (pos? dt)
|
|
325
|
+
(let [active-before (js-call core "active_transition_count")
|
|
326
|
+
active-after (js-call core "update" dt)]
|
|
327
|
+
(js-call morph-transitions "tick" dt)
|
|
328
|
+
(when (or (pos? active-before) (pos? active-after))
|
|
329
|
+
(apply-frame!))
|
|
330
|
+
(when @animation-system
|
|
331
|
+
(js-call @animation-system "update" dt))
|
|
332
|
+
(when @hair-runtime
|
|
333
|
+
(js-call @hair-runtime "update" dt))))))
|
|
334
|
+
|
|
335
|
+
(request-next-frame! []
|
|
336
|
+
(when @running?
|
|
337
|
+
(when-let [request-frame (aget js/globalThis "requestAnimationFrame")]
|
|
338
|
+
(reset! raf-id
|
|
339
|
+
(.call request-frame
|
|
340
|
+
js/globalThis
|
|
341
|
+
(fn [now-ms]
|
|
342
|
+
(let [previous @last-frame-ms
|
|
343
|
+
dt (if previous
|
|
344
|
+
(/ (max 0 (- now-ms previous)) 1000)
|
|
345
|
+
0)]
|
|
346
|
+
(reset! last-frame-ms now-ms)
|
|
347
|
+
(update! dt)
|
|
348
|
+
(request-next-frame!))))))))
|
|
349
|
+
|
|
350
|
+
(stop! []
|
|
351
|
+
(reset! running? false)
|
|
352
|
+
(reset! last-frame-ms nil)
|
|
353
|
+
(when (some? @raf-id)
|
|
354
|
+
(when-let [cancel-frame (aget js/globalThis "cancelAnimationFrame")]
|
|
355
|
+
(.call cancel-frame js/globalThis @raf-id))
|
|
356
|
+
(reset! raf-id nil)))
|
|
357
|
+
|
|
358
|
+
(hair-meshes []
|
|
359
|
+
(filter
|
|
360
|
+
(fn [mesh]
|
|
361
|
+
(let [info (aget (profile-map @profile "meshes") (.-name mesh))
|
|
362
|
+
category (when info (aget info "category"))]
|
|
363
|
+
(or (= category "hair") (= category "eyebrow"))))
|
|
364
|
+
(js-values @all-meshes)))]
|
|
365
|
+
(let [animation-host
|
|
366
|
+
#js {:getModel (fn [] @model)
|
|
367
|
+
:getMeshes (fn [] @meshes)
|
|
368
|
+
:getMeshByName (fn [name] (.get @mesh-by-name name))
|
|
369
|
+
:getMeshNamesForAU
|
|
370
|
+
(fn [au-id] (get-mesh-names-for-au-profile @profile au-id))
|
|
371
|
+
:getMeshNamesForViseme
|
|
372
|
+
(fn [] (get-mesh-names-for-viseme-profile @profile))
|
|
373
|
+
:getCurrentAUValue (fn [au-id] (js-call core "get_au" au-id))
|
|
374
|
+
:getCurrentVisemeValue
|
|
375
|
+
(fn [index] (or (aget @viseme-values index) 0))
|
|
376
|
+
:getCurrentMorphValue
|
|
377
|
+
(fn [morph-key mesh-names]
|
|
378
|
+
(current-morph-value
|
|
379
|
+
@meshes @mesh-by-name @profile morph-key mesh-names))
|
|
380
|
+
:getCurrentMorphIndexValue
|
|
381
|
+
(fn [morph-index mesh-names]
|
|
382
|
+
(current-morph-value
|
|
383
|
+
@meshes @mesh-by-name @profile morph-index mesh-names))
|
|
384
|
+
:getCurrentBoneQuaternion
|
|
385
|
+
(fn [node-key]
|
|
386
|
+
(some-> (aget @bones node-key) (aget "obj") (aget "quaternion") (.clone)))
|
|
387
|
+
:getCurrentBonePositionValue
|
|
388
|
+
(fn [node-key axis]
|
|
389
|
+
(some-> (aget @bones node-key) (aget "obj") (aget "position") (aget axis)))
|
|
390
|
+
:getBones (fn [] @bones)
|
|
391
|
+
:getConfig (fn [] @profile)
|
|
392
|
+
:getCompositeRotations
|
|
393
|
+
(fn [] (profile-array @profile "compositeRotations"))
|
|
394
|
+
:computeSideValues
|
|
395
|
+
(fn [base balance]
|
|
396
|
+
(let [packed (embody-wasm/solve-bilateral-values base (or balance 0))]
|
|
397
|
+
#js {:left (aget packed 0) :right (aget packed 1)}))
|
|
398
|
+
:getAUMixWeight get-au-mix-weight
|
|
399
|
+
:isMixedAU (fn [au-id] (mixed-au? @profile au-id))
|
|
400
|
+
:reapplyProceduralState apply-frame!}]
|
|
401
|
+
(reset! animation-system (js-new ThreeAnimationSystem animation-host)))
|
|
402
|
+
|
|
403
|
+
(let [hair-host
|
|
404
|
+
#js {:getMeshByName (fn [name] (.get @mesh-by-name name))
|
|
405
|
+
:getSelectedHairMeshNames
|
|
406
|
+
(fn []
|
|
407
|
+
(to-array
|
|
408
|
+
(map #(aget % "name")
|
|
409
|
+
(filter
|
|
410
|
+
(fn [mesh]
|
|
411
|
+
(= "hair"
|
|
412
|
+
(some-> (aget (profile-map @profile "meshes")
|
|
413
|
+
(aget mesh "name"))
|
|
414
|
+
(aget "category"))))
|
|
415
|
+
(js-values @all-meshes)))))
|
|
416
|
+
:buildClip
|
|
417
|
+
(fn [clip-name curves options]
|
|
418
|
+
(js-call @animation-system "buildClip" clip-name curves options))
|
|
419
|
+
:cleanupSnippet
|
|
420
|
+
(fn [clip-name]
|
|
421
|
+
(js-call @animation-system "cleanupSnippet" clip-name))}
|
|
422
|
+
controller (js-new HairPhysicsController hair-host)]
|
|
423
|
+
(js-call controller "setCore" wasm)
|
|
424
|
+
(reset! hair-runtime controller))
|
|
425
|
+
|
|
426
|
+
(aset host "onReady"
|
|
427
|
+
(fn [options]
|
|
428
|
+
(reset! model (aget options "model"))
|
|
429
|
+
(bind-model! (aget options "meshes"))))
|
|
430
|
+
(aset host "bindModel" bind-model!)
|
|
431
|
+
(aset host "setProfile" set-profile!)
|
|
432
|
+
(aset host "getProfile" (fn [] @profile))
|
|
433
|
+
(aset host "getProfileMode" (fn [] profile-mode))
|
|
434
|
+
(aset host "supportsPreset" (fn [id] (supports-preset? id)))
|
|
435
|
+
(aset host "setAU"
|
|
436
|
+
(fn [id value balance]
|
|
437
|
+
(js-call core "set_au_signed" id value (if (number? balance) balance js/NaN))
|
|
438
|
+
(apply-frame!)))
|
|
439
|
+
(aset host "getAU" (fn [id] (js-call core "get_au" id)))
|
|
440
|
+
(aset host "setContinuum"
|
|
441
|
+
(fn [negative-au positive-au value balance]
|
|
442
|
+
(js-call core
|
|
443
|
+
"set_continuum"
|
|
444
|
+
negative-au
|
|
445
|
+
positive-au
|
|
446
|
+
value
|
|
447
|
+
(if (number? balance) balance js/NaN))
|
|
448
|
+
(apply-frame!)))
|
|
449
|
+
(aset host "getContinuum"
|
|
450
|
+
(fn [negative-au positive-au]
|
|
451
|
+
(js-call core "get_continuum" negative-au positive-au)))
|
|
452
|
+
(aset host "transitionAU"
|
|
453
|
+
(fn [id value duration-ms balance]
|
|
454
|
+
(js-call core
|
|
455
|
+
"transition_au"
|
|
456
|
+
id
|
|
457
|
+
value
|
|
458
|
+
(or duration-ms 200)
|
|
459
|
+
(if (number? balance) balance js/NaN))))
|
|
460
|
+
(aset host "transitionViseme"
|
|
461
|
+
(fn [index value duration-ms jaw-scale]
|
|
462
|
+
(js-call core
|
|
463
|
+
"transition_viseme"
|
|
464
|
+
index
|
|
465
|
+
value
|
|
466
|
+
(or duration-ms 80)
|
|
467
|
+
(or jaw-scale 1))))
|
|
468
|
+
(aset host "setViseme"
|
|
469
|
+
(fn [index value jaw-scale]
|
|
470
|
+
(js-call core "set_viseme" index value)
|
|
471
|
+
(js-call core "set_viseme_jaw_scale" index (or jaw-scale 1))
|
|
472
|
+
(aset @viseme-values index value)
|
|
473
|
+
(apply-frame!)))
|
|
474
|
+
(aset host "setVisemeById"
|
|
475
|
+
(fn [slot-id value jaw-scale]
|
|
476
|
+
(let [index (js-call core "viseme_slot_index" slot-id)]
|
|
477
|
+
(when (<= 0 index)
|
|
478
|
+
(js-call host "setViseme" index value (or jaw-scale 1))))))
|
|
479
|
+
(aset host "transitionVisemeById"
|
|
480
|
+
(fn [slot-id value duration-ms jaw-scale]
|
|
481
|
+
(let [index (js-call core "viseme_slot_index" slot-id)]
|
|
482
|
+
(when (<= 0 index)
|
|
483
|
+
(js-call host
|
|
484
|
+
"transitionViseme"
|
|
485
|
+
index
|
|
486
|
+
value
|
|
487
|
+
(or duration-ms 80)
|
|
488
|
+
(or jaw-scale 1))))))
|
|
489
|
+
(aset host "setAUMixWeight"
|
|
490
|
+
(fn [id weight]
|
|
491
|
+
(swap! mix-weights assoc id weight)
|
|
492
|
+
(js-call core "set_au_mix_weight" id weight)
|
|
493
|
+
(apply-frame!)))
|
|
494
|
+
(aset host "getAUMixWeight" get-au-mix-weight)
|
|
495
|
+
(aset host "hasLeftRightBones"
|
|
496
|
+
(fn [au-id]
|
|
497
|
+
(let [bindings (aget (profile-map @profile "auToBones") (str au-id))
|
|
498
|
+
sides (set (map #(aget % "side") (js-values bindings)))]
|
|
499
|
+
(and (contains? sides "left") (contains? sides "right")))))
|
|
500
|
+
(aset host "setMorph"
|
|
501
|
+
(fn
|
|
502
|
+
([morph-key value] (set-morph! morph-key value nil))
|
|
503
|
+
([morph-key value mesh-names] (set-morph! morph-key value mesh-names))))
|
|
504
|
+
(aset host "setMorphOnMeshes"
|
|
505
|
+
(fn [mesh-names morph-key value]
|
|
506
|
+
(set-morph! morph-key value mesh-names)))
|
|
507
|
+
(aset host "transitionMorph"
|
|
508
|
+
(fn
|
|
509
|
+
([morph-key value] (transition-morph! morph-key value 120 nil))
|
|
510
|
+
([morph-key value duration-ms]
|
|
511
|
+
(transition-morph! morph-key value duration-ms nil))
|
|
512
|
+
([morph-key value duration-ms mesh-names]
|
|
513
|
+
(transition-morph! morph-key value duration-ms mesh-names))))
|
|
514
|
+
(aset host "getMorphTargets"
|
|
515
|
+
(fn []
|
|
516
|
+
(let [result (js-obj)]
|
|
517
|
+
(doseq [mesh (js-values @meshes)]
|
|
518
|
+
(when-let [dictionary (aget mesh "morphTargetDictionary")]
|
|
519
|
+
(aset result
|
|
520
|
+
(aget mesh "name")
|
|
521
|
+
(.sort (.keys js/Object dictionary)))))
|
|
522
|
+
result)))
|
|
523
|
+
(aset host "getMorphTargetIndices"
|
|
524
|
+
(fn []
|
|
525
|
+
(let [result (js-obj)]
|
|
526
|
+
(doseq [mesh (js-values @meshes)]
|
|
527
|
+
(when-let [dictionary (aget mesh "morphTargetDictionary")]
|
|
528
|
+
(let [entries (.map
|
|
529
|
+
(.entries js/Object dictionary)
|
|
530
|
+
(fn [entry]
|
|
531
|
+
#js {:name (aget entry 0) :index (aget entry 1)}))]
|
|
532
|
+
(.sort entries (fn [a b] (- (.-index a) (.-index b))))
|
|
533
|
+
(aset result (aget mesh "name") entries))))
|
|
534
|
+
result)))
|
|
535
|
+
(aset host "getHairMorphTargets"
|
|
536
|
+
(fn
|
|
537
|
+
([] (js-call @hair-runtime "getHairMorphTargets"))
|
|
538
|
+
([mesh-name] (js-call @hair-runtime "getHairMorphTargets" mesh-name))))
|
|
539
|
+
(aset host "getMeshList"
|
|
540
|
+
(fn []
|
|
541
|
+
(.map
|
|
542
|
+
@all-meshes
|
|
543
|
+
(fn [mesh]
|
|
544
|
+
(let [mesh-name (aget mesh "name")
|
|
545
|
+
info (aget (profile-map @profile "meshes") mesh-name)]
|
|
546
|
+
#js {:name mesh-name
|
|
547
|
+
:visible (aget mesh "visible")
|
|
548
|
+
:morphCount (.-length (or (aget mesh "morphTargetInfluences") #js []))
|
|
549
|
+
:category (or (when info (aget info "category")) "other")})))))
|
|
550
|
+
(aset host "getBones" (fn [] @bones))
|
|
551
|
+
(aset host "setMeshVisible"
|
|
552
|
+
(fn [mesh-name visible]
|
|
553
|
+
(when @model
|
|
554
|
+
(js-call applier "setMeshVisible" @model mesh-name visible))))
|
|
555
|
+
(aset host "highlightMesh"
|
|
556
|
+
(fn
|
|
557
|
+
([mesh-name]
|
|
558
|
+
(when @model (js-call applier "highlightMesh" @model mesh-name)))
|
|
559
|
+
([mesh-name color]
|
|
560
|
+
(when @model (js-call applier "highlightMesh" @model mesh-name color)))
|
|
561
|
+
([mesh-name color intensity]
|
|
562
|
+
(when @model
|
|
563
|
+
(js-call applier "highlightMesh" @model mesh-name color intensity)))))
|
|
564
|
+
(aset host "getMeshMaterialConfig"
|
|
565
|
+
(fn [mesh-name]
|
|
566
|
+
(when @model
|
|
567
|
+
(js-call applier "getMeshMaterialConfig" @model mesh-name))))
|
|
568
|
+
(aset host "setMeshMaterialConfig"
|
|
569
|
+
(fn [mesh-name material-config]
|
|
570
|
+
(when @model
|
|
571
|
+
(js-call applier
|
|
572
|
+
"setMeshMaterialConfig"
|
|
573
|
+
@model
|
|
574
|
+
mesh-name
|
|
575
|
+
material-config))))
|
|
576
|
+
(doseq [method ["registerHairObjects"
|
|
577
|
+
"getRegisteredHairObjects"
|
|
578
|
+
"getHairAppearance"
|
|
579
|
+
"setHairColor"
|
|
580
|
+
"setEyebrowColor"
|
|
581
|
+
"setHairBaseColor"
|
|
582
|
+
"setEyebrowBaseColor"
|
|
583
|
+
"setHairGlow"
|
|
584
|
+
"setEyebrowGlow"
|
|
585
|
+
"setHairOutline"
|
|
586
|
+
"setHairPhysicsEnabled"
|
|
587
|
+
"isHairPhysicsEnabled"
|
|
588
|
+
"setHairPhysicsConfig"
|
|
589
|
+
"getHairPhysicsConfig"
|
|
590
|
+
"validateHairMorphTargets"
|
|
591
|
+
"applyHairStateToObject"]]
|
|
592
|
+
(aset host method
|
|
593
|
+
(fn [& args]
|
|
594
|
+
(apply js-call @hair-runtime method args))))
|
|
595
|
+
(aset host "updateHairPhysics"
|
|
596
|
+
(fn [dt-seconds]
|
|
597
|
+
(js-call @hair-runtime "update" dt-seconds)))
|
|
598
|
+
(aset host "update" update!)
|
|
599
|
+
(aset host "start"
|
|
600
|
+
(fn []
|
|
601
|
+
(when-not @running?
|
|
602
|
+
(reset! running? true)
|
|
603
|
+
(reset! last-frame-ms nil)
|
|
604
|
+
(request-next-frame!))))
|
|
605
|
+
(aset host "stop" stop!)
|
|
606
|
+
(aset host "clear"
|
|
607
|
+
(fn []
|
|
608
|
+
(js-call core "clear")
|
|
609
|
+
(js-call morph-transitions "clearTransitions")
|
|
610
|
+
(apply-frame!)))
|
|
611
|
+
(aset host "resetToNeutral" (aget host "clear"))
|
|
612
|
+
(aset host "dispose"
|
|
613
|
+
(fn []
|
|
614
|
+
(stop!)
|
|
615
|
+
(when @animation-system
|
|
616
|
+
(js-call @animation-system "dispose"))
|
|
617
|
+
(when @hair-runtime
|
|
618
|
+
(js-call @hair-runtime "clearRegisteredHairObjects"))
|
|
619
|
+
(when-let [free (aget core "free")]
|
|
620
|
+
(.call free core))
|
|
621
|
+
(reset! model nil)
|
|
622
|
+
(reset! meshes #js [])
|
|
623
|
+
(reset! all-meshes #js [])
|
|
624
|
+
(reset! mesh-by-name (js/Map.))
|
|
625
|
+
(reset! bones #js {})))
|
|
626
|
+
|
|
627
|
+
(doseq [method animation-methods]
|
|
628
|
+
(animation-method! host animation-system method))
|
|
629
|
+
host))))
|
|
630
|
+
|
|
631
|
+
(def createRustEmbodyHostUnbound create-rust-embody-host-unbound)
|
|
632
|
+
|
|
633
|
+
(defn create-rust-embody-host
|
|
634
|
+
"Create a Polymer CLJS host and bind an already-loaded Three.js model."
|
|
635
|
+
([model]
|
|
636
|
+
(create-rust-embody-host model #js {}))
|
|
637
|
+
([model config]
|
|
638
|
+
(-> (embody-wasm/init-core!)
|
|
639
|
+
(.then
|
|
640
|
+
(fn [_]
|
|
641
|
+
(let [host (create-rust-embody-host-unbound config)]
|
|
642
|
+
(js-call host "onReady" #js {:model model :meshes (aget config "meshes")})
|
|
643
|
+
host))))))
|
|
644
|
+
|
|
645
|
+
(def createRustEmbodyHost create-rust-embody-host)
|