wco_models 3.1.0.324 → 3.1.0.326
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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/production_theme.scss +4 -1
- data/app/assets/stylesheets/wco/main.scss +4 -0
- data/app/assets/stylesheets/wco/studio.scss +12 -4
- data/app/controllers/wco/api/newspartials_controller.rb +1 -1
- data/app/controllers/wco/api/newsproducer_controller.rb +5 -0
- data/app/controllers/wco/newspartials_controller.rb +1 -0
- data/app/models/iro/datapoint.rb +1 -1
- data/app/models/iro/option.rb +16 -2
- data/app/models/iro/position.rb +4 -6
- data/app/models/iro/priceitem.rb +42 -4
- data/app/models/iro/purse.rb +2 -0
- data/app/models/iro/stock.rb +1 -2
- data/app/models/tda/option.rb +4 -9
- data/app/models/wco/newspartial.rb +12 -4
- data/app/views/wco/api/newsproducer/README_newsproducer.txt +12 -0
- data/app/views/wco/api/newsproducer/_scenes.js-bk +20 -0
- data/app/views/wco/api/newsproducer/_scenes.js.erb +15 -0
- data/app/views/wco/api/newsproducer/{_studio_blue.js → _studio_blue1.js} +4 -1
- data/app/views/wco/api/newsproducer/_studio_blue2.js +912 -0
- data/app/views/wco/api/newsproducer/_studio_coc.js +769 -0
- data/app/views/wco/api/newsproducer/_studio_coc.js-bk +389 -0
- data/app/views/wco/api/newsproducer/_studio_coc.js-cannon +677 -0
- data/app/views/wco/api/newsproducer/_studio_fps.js +1018 -0
- data/app/views/wco/api/newsproducer/_studio_ra2.js +255 -0
- data/app/views/wco/api/newsproducer/_touch_controls.js +708 -0
- data/app/views/wco/api/newsproducer/_touch_pad.css +200 -0
- data/app/views/wco/api/newsproducer/games_fps.html +504 -0
- data/app/views/wco/api/newsproducer/{studio_green.html.erb → studio_blue0.html.erb} +1 -1
- data/app/views/wco/api/newsproducer/{studio_blue.html.erb → studio_blue1.html.erb} +3 -3
- data/app/views/wco/api/newsproducer/studio_blue2.html.erb +146 -0
- data/app/views/wco/api/newsproducer/studio_coc.html.erb +95 -0
- data/app/views/wco/api/newsproducer/studio_fps.html.erb +199 -0
- data/app/views/wco/api/newsproducer/studio_ra2.html.erb +95 -0
- data/app/views/wco/newspartials/_show_in_newsvideo.haml +6 -7
- data/app/views/wco/newspartials/index.haml-trash +6 -0
- data/config/initializers/08_integrations.rb +14 -1
- data/config/initializers/08_integrations.rb-example +0 -2
- data/config/routes.rb +3 -0
- data/lib/shortcuts.rb +1 -1
- data/lib/tasks/office_tasks.rake +7 -5
- metadata +23 -7
- data/app/views/wco/api/newsproducer/README.txt +0 -2
- /data/app/views/wco/api/newsproducer/{_studio_green.js → _studio_blue0.js} +0 -0
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
|
|
2
|
+
/*
|
|
3
|
+
**/
|
|
4
|
+
const logg = (a, b="", c=null) => {
|
|
5
|
+
if ('undefined' === typeof window) { return }
|
|
6
|
+
c = "string" === typeof c ? c : b.replace(/\W/g, '')
|
|
7
|
+
if (c.length > 0) {
|
|
8
|
+
window[c] = a
|
|
9
|
+
}
|
|
10
|
+
console.log(`+++ ${b}:`, a) // eslint-disable-line no-console
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const MODELS_ROOT = 'https://localhost/vendor/models';
|
|
14
|
+
// const AVATARS_ROOT = 'https://cdn.jsdelivr.net/gh/wasya-co/ishlib3js@0.3.0/public/vendor/models/avatars';
|
|
15
|
+
const AVATARS_ROOT = 'https://localhost/vendor/models/avatars';
|
|
16
|
+
const avatars = {
|
|
17
|
+
brunette: { type: 'F', url: `${AVATARS_ROOT}/brunette/model.glb` },
|
|
18
|
+
cara: { type: 'F', url: `${AVATARS_ROOT}/cara/model.glb` }, // short hair
|
|
19
|
+
eve: { type: 'F', url: `${AVATARS_ROOT}/eve/model.glb` }, // poison green
|
|
20
|
+
|
|
21
|
+
denis: { type: 'M', url: `${AVATARS_ROOT}/denis/model.glb` }, // beard
|
|
22
|
+
gregor: { type: 'M', url: `${AVATARS_ROOT}/gregor/model.glb` }, // gray shirt
|
|
23
|
+
}
|
|
24
|
+
const AVATAR_STOR = 'avatar'
|
|
25
|
+
$('select.avatar').each((_idx, el) => {
|
|
26
|
+
const $select = $(el)
|
|
27
|
+
$.each(avatars, function(name, url) {
|
|
28
|
+
$('<option>', {
|
|
29
|
+
value: name,
|
|
30
|
+
text: name,
|
|
31
|
+
}).appendTo($select)
|
|
32
|
+
})
|
|
33
|
+
try {
|
|
34
|
+
const saved = localStorage.getItem(`${AVATAR_STOR}-${$select.data('uid')}`)
|
|
35
|
+
if (saved && avatars[saved]) $select.val(saved)
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.log(error)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
let scenes = {
|
|
42
|
+
skybox_1: {
|
|
43
|
+
url: `${MODELS_ROOT}/scenes/000mb skybox_1/scene.glb`,
|
|
44
|
+
height: 30,
|
|
45
|
+
},
|
|
46
|
+
studio_tron: {
|
|
47
|
+
height: 10,
|
|
48
|
+
url: `${MODELS_ROOT}/scenes/003mb studio_tron/scene.glb`,
|
|
49
|
+
},
|
|
50
|
+
room_1: {
|
|
51
|
+
height: 6,
|
|
52
|
+
url: `${MODELS_ROOT}/scenes/000mb room-1/scene.glb`,
|
|
53
|
+
},
|
|
54
|
+
studio_blue: {
|
|
55
|
+
height: 4.3,
|
|
56
|
+
url: `${MODELS_ROOT}/scenes/000mb studio_blue/scene.glb`,
|
|
57
|
+
},
|
|
58
|
+
purple_stage: {
|
|
59
|
+
height: 10,
|
|
60
|
+
url: `${MODELS_ROOT}/scenes/000mb purple_stage/scene.glb`,
|
|
61
|
+
},
|
|
62
|
+
red_stage: {
|
|
63
|
+
height: 10,
|
|
64
|
+
url: `${MODELS_ROOT}/scenes/000mb red_stage/scene.glb`,
|
|
65
|
+
},
|
|
66
|
+
// skybox_1: `${MODELS_ROOT}/scenes//scene.glb`,
|
|
67
|
+
|
|
68
|
+
newsroom_green: `${MODELS_ROOT}/scenes/001mb newsroom_green/scene.glb`,
|
|
69
|
+
rick_and_morty_garage: `${MODELS_ROOT}/scenes/003mb rick-and-morty-garage/scene.glb`,
|
|
70
|
+
}
|
|
71
|
+
const SCENE_STOR = 'studio'
|
|
72
|
+
$.each(scenes, (name) => {
|
|
73
|
+
$('<option>', { value: name, text: name }).appendTo($('select.studio'))
|
|
74
|
+
})
|
|
75
|
+
try {
|
|
76
|
+
const saved = localStorage.getItem( SCENE_STOR )
|
|
77
|
+
if (saved && scenes[saved]) $('select.studio').val(saved)
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.log(error)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let scene_url = scenes[$('select.studio').val()] || scenes.rick_and_morty_garage
|
|
83
|
+
|
|
84
|
+
let gestures = {
|
|
85
|
+
talking_1: (avatar) => `${MODELS_ROOT}/animation-library/feminine/fbx/expression/${avatar.body}_Talking_Variations_001.fbx`,
|
|
86
|
+
talking_4: (avatar) => `${MODELS_ROOT}/animation-library/feminine/fbx/expression/${avatar.body}_Talking_Variations_004.fbx`,
|
|
87
|
+
walk_1: { speed: 2.0, url: (a) => `${MODELS_ROOT}/animation-library/feminine/fbx/locomotion/${a.body}_Walk_001.fbx` },
|
|
88
|
+
walk_2: { speed: 2.0, url: (a) => `${MODELS_ROOT}/animation-library/feminine/fbx/locomotion/${a.body}_Walk_002.fbx` },
|
|
89
|
+
|
|
90
|
+
sitting: (a) => `${MODELS_ROOT}/animations/Sitting.fbx`,
|
|
91
|
+
sit_to_stand: (a) => `${MODELS_ROOT}/animations/Sit To Stand.fbx`,
|
|
92
|
+
stand_to_sit: (a) => `${MODELS_ROOT}/animations/Stand To Sit.fbx`,
|
|
93
|
+
|
|
94
|
+
entering_car: (a) => `${MODELS_ROOT}/animations/Entering Car.fbx`,
|
|
95
|
+
slow_run: { speed: 1.1, url: (a) => `${MODELS_ROOT}/animations/Slow Run.fbx`, }
|
|
96
|
+
};
|
|
97
|
+
$.each(gestures, (name) => {
|
|
98
|
+
$('<option>', { value: name, text: name }).appendTo($('select.gestures'))
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
let u_positions = {
|
|
102
|
+
'1': [0, 0, 0],
|
|
103
|
+
'2': [1, 0, 0],
|
|
104
|
+
'3': [-1, 0, 0],
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
import * as THREE from 'three'
|
|
110
|
+
|
|
111
|
+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
|
|
112
|
+
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js'
|
|
113
|
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
|
114
|
+
|
|
115
|
+
import { TalkingHead } from "talkinghead"
|
|
116
|
+
|
|
117
|
+
const params = new URLSearchParams(window.location.search)
|
|
118
|
+
|
|
119
|
+
let config = {
|
|
120
|
+
sampleRate: 8000,
|
|
121
|
+
mood: 'neutral',
|
|
122
|
+
gain: 0.5,
|
|
123
|
+
lipsyncType: "visemes",
|
|
124
|
+
lipsyncLang: "en",
|
|
125
|
+
waitForAudioChunks: false,
|
|
126
|
+
enableMetrics: false,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const onMetrics = (which) => {
|
|
130
|
+
logg(which, 'onMetrics')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const onSubtitles = (which) => {
|
|
134
|
+
logg(which, 'onSubtitles')
|
|
135
|
+
}
|
|
136
|
+
const streamOpts = {
|
|
137
|
+
gain: config.gain,
|
|
138
|
+
lipsyncLang: config.lipsyncLang,
|
|
139
|
+
lipsyncType: config.lipsyncType,
|
|
140
|
+
metrics: config.enableMetrics ? { enabled: true, intervalHz: config.metricsInterval } : { enabled: false },
|
|
141
|
+
mood: config.mood,
|
|
142
|
+
sampleRate: config.sampleRate,
|
|
143
|
+
waitForAudioChunks: config.waitForAudioChunks,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
let width = 854
|
|
148
|
+
let height = 480
|
|
149
|
+
let slug = '<ccapture>'
|
|
150
|
+
const fps = 30
|
|
151
|
+
|
|
152
|
+
let frame = 0
|
|
153
|
+
let lastAnimTime = 0
|
|
154
|
+
let capturing = false
|
|
155
|
+
let captureFrame = 0
|
|
156
|
+
let captureTotal = 0
|
|
157
|
+
const api_key = params.get('api_key')
|
|
158
|
+
const api_secret = params.get('api_secret')
|
|
159
|
+
const wco_origin = params.get('wco_origin')
|
|
160
|
+
const newspartial_id = params.get('newspartial_id')
|
|
161
|
+
let totalFrames
|
|
162
|
+
|
|
163
|
+
let camera, camera_wide, camera_1, camera_2, camera_3
|
|
164
|
+
let controls, renderer, scene
|
|
165
|
+
let head, head_1, head_2, head_3
|
|
166
|
+
let faceTarget = new THREE.Vector3()
|
|
167
|
+
const cameraTargets = {
|
|
168
|
+
wide: new THREE.Vector3(0.5, 1.2, 0),
|
|
169
|
+
av_1: new THREE.Vector3(0, 1.5, 0),
|
|
170
|
+
av_2: new THREE.Vector3(1, 1.5, 0),
|
|
171
|
+
av_3: new THREE.Vector3(-1, 1.5, 0),
|
|
172
|
+
}
|
|
173
|
+
let cameraTransition = null
|
|
174
|
+
const CAMERA_BLEND_MS = 900
|
|
175
|
+
let chunkedInput = null
|
|
176
|
+
const lights = {}
|
|
177
|
+
const LIGHT_CTRL_KEY = 'wco.studio_blue.light-ctrl'
|
|
178
|
+
const HEAD_CTRL_KEY = 'wco.studio_blue.head-ctrl'
|
|
179
|
+
const CAMERA_CTRL_KEY = 'wco.studio_blue.camera-ctrl'
|
|
180
|
+
var capturer = new CCapture( { format: 'webm', framerate: fps } )
|
|
181
|
+
const gltfLoader = new GLTFLoader()
|
|
182
|
+
const fbxLoader = new FBXLoader()
|
|
183
|
+
const loading = document.getElementById('loading')
|
|
184
|
+
const status = document.getElementById('status')
|
|
185
|
+
|
|
186
|
+
/*
|
|
187
|
+
**/
|
|
188
|
+
function point_camera_at_face(cam_id, _head) {
|
|
189
|
+
const leftEye = new THREE.Vector3()
|
|
190
|
+
const rightEye = new THREE.Vector3()
|
|
191
|
+
_head.objectLeftEye.getWorldPosition(leftEye)
|
|
192
|
+
_head.objectRightEye.getWorldPosition(rightEye)
|
|
193
|
+
faceTarget.copy(leftEye).add(rightEye).multiplyScalar(0.5)
|
|
194
|
+
|
|
195
|
+
const camPos = new THREE.Vector3(0, 0, 2)
|
|
196
|
+
camPos.applyQuaternion(_head.armature.quaternion)
|
|
197
|
+
camPos.add(faceTarget)
|
|
198
|
+
const _camera = cameras_fn(cam_id)
|
|
199
|
+
_camera.position.copy(camPos)
|
|
200
|
+
_camera.lookAt(faceTarget)
|
|
201
|
+
cameraTargets[cam_id].copy(faceTarget)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/*
|
|
205
|
+
**/
|
|
206
|
+
function put_feet_at_origin(_head) {
|
|
207
|
+
// Put feet at the origin
|
|
208
|
+
_head.armature.updateMatrixWorld(true)
|
|
209
|
+
const leftToe = new THREE.Vector3()
|
|
210
|
+
const rightToe = new THREE.Vector3()
|
|
211
|
+
_head.objectLeftToeBase.getWorldPosition(leftToe)
|
|
212
|
+
_head.objectRightToeBase.getWorldPosition(rightToe)
|
|
213
|
+
_head.armature.position.set(
|
|
214
|
+
-(leftToe.x + rightToe.x) / 2,
|
|
215
|
+
-Math.min(leftToe.y, rightToe.y),
|
|
216
|
+
-(leftToe.z + rightToe.z) / 2
|
|
217
|
+
)
|
|
218
|
+
_head.armature.updateMatrixWorld(true)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/*
|
|
222
|
+
**/
|
|
223
|
+
function rescale(model, config) {
|
|
224
|
+
const box = new THREE.Box3().setFromObject(model)
|
|
225
|
+
const size = box.getSize(new THREE.Vector3())
|
|
226
|
+
const currentHeight = size.y
|
|
227
|
+
const scale = config.height / currentHeight
|
|
228
|
+
model.scale.setScalar(scale)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function scene_cfg(s) {
|
|
232
|
+
if (typeof s === 'string') return { url: s, height: 3.3 }
|
|
233
|
+
return { url: s.url, height: s.height || 3.3 }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function set_shadows(root, cast, receive) {
|
|
237
|
+
root.traverse((obj) => {
|
|
238
|
+
if (obj.isMesh) {
|
|
239
|
+
obj.castShadow = cast
|
|
240
|
+
obj.receiveShadow = receive
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/*
|
|
246
|
+
**/
|
|
247
|
+
function setup_light(scene) {
|
|
248
|
+
lights.ambientLight = new THREE.AmbientLight( 0xffffff, 0.6 )
|
|
249
|
+
scene.add( lights.ambientLight )
|
|
250
|
+
|
|
251
|
+
lights.hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 1.2 )
|
|
252
|
+
lights.hemiLight.position.set( 0, 20, 0 )
|
|
253
|
+
scene.add( lights.hemiLight )
|
|
254
|
+
|
|
255
|
+
lights.keyLight = new THREE.DirectionalLight( 0xffffff, 2.5 )
|
|
256
|
+
lights.keyLight.position.set( 5, 10, 7 )
|
|
257
|
+
lights.keyLight.castShadow = true
|
|
258
|
+
lights.keyLight.shadow.mapSize.set( 2048, 2048 )
|
|
259
|
+
lights.keyLight.shadow.camera.near = 0.5
|
|
260
|
+
lights.keyLight.shadow.camera.far = 40
|
|
261
|
+
lights.keyLight.shadow.camera.left = -10
|
|
262
|
+
lights.keyLight.shadow.camera.right = 10
|
|
263
|
+
lights.keyLight.shadow.camera.top = 10
|
|
264
|
+
lights.keyLight.shadow.camera.bottom = -10
|
|
265
|
+
lights.keyLight.shadow.bias = -0.0001
|
|
266
|
+
scene.add( lights.keyLight )
|
|
267
|
+
|
|
268
|
+
lights.fillLight = new THREE.DirectionalLight( 0xffffff, 1.2 )
|
|
269
|
+
lights.fillLight.position.set( -5, 4, -2 )
|
|
270
|
+
scene.add( lights.fillLight )
|
|
271
|
+
|
|
272
|
+
lights.rimLight = new THREE.DirectionalLight( 0xffffff, 0.8 )
|
|
273
|
+
lights.rimLight.position.set( 0, 6, -8 )
|
|
274
|
+
scene.add( lights.rimLight )
|
|
275
|
+
|
|
276
|
+
restoreLightControls()
|
|
277
|
+
syncLightsFromControls()
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function restoreLightControls() {
|
|
281
|
+
try {
|
|
282
|
+
const saved = JSON.parse(localStorage.getItem(LIGHT_CTRL_KEY) || 'null')
|
|
283
|
+
if (!saved || typeof saved !== 'object') return
|
|
284
|
+
document.querySelectorAll('input.light-ctrl').forEach((input) => {
|
|
285
|
+
if (Object.prototype.hasOwnProperty.call(saved, input.name)) {
|
|
286
|
+
input.checked = !!saved[input.name]
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.log(error)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function persistLightControls() {
|
|
295
|
+
const saved = {}
|
|
296
|
+
document.querySelectorAll('input.light-ctrl').forEach((input) => {
|
|
297
|
+
saved[input.name] = input.checked
|
|
298
|
+
})
|
|
299
|
+
localStorage.setItem(LIGHT_CTRL_KEY, JSON.stringify(saved))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function restoreHeadControl() {
|
|
303
|
+
try {
|
|
304
|
+
const saved = localStorage.getItem(HEAD_CTRL_KEY)
|
|
305
|
+
if (!saved) return
|
|
306
|
+
const input = document.querySelector(`input[name=head][value="${saved}"]`)
|
|
307
|
+
if (input) input.checked = true
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.log(error)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function persistHeadControl(id) {
|
|
314
|
+
localStorage.setItem(HEAD_CTRL_KEY, String(id))
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function restoreCameraControl() {
|
|
318
|
+
try {
|
|
319
|
+
const saved = localStorage.getItem(CAMERA_CTRL_KEY)
|
|
320
|
+
if (!saved) return
|
|
321
|
+
const input = document.querySelector(`input[name=camera][value="${saved}"]`)
|
|
322
|
+
if (input) input.checked = true
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.log(error)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function persistCameraControl(id) {
|
|
329
|
+
localStorage.setItem(CAMERA_CTRL_KEY, String(id))
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function syncLightsFromControls() {
|
|
333
|
+
document.querySelectorAll('input.light-ctrl').forEach((input) => {
|
|
334
|
+
const light = lights[input.name]
|
|
335
|
+
if (light) light.visible = input.checked
|
|
336
|
+
})
|
|
337
|
+
persistLightControls()
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function sync_shadows() {
|
|
341
|
+
const on = $('#is_shadows').prop('checked')
|
|
342
|
+
if (lights.keyLight) lights.keyLight.castShadow = on
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function gesture_url(g, avatar) {
|
|
346
|
+
if (typeof g === 'function') return g(avatar)
|
|
347
|
+
const u = g && g.url
|
|
348
|
+
return typeof u === 'function' ? u(avatar) : u
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function update_walk(this_head, dt) {
|
|
352
|
+
if (!this_head || !this_head.speed || !this_head.mixer || !this_head.armature) return
|
|
353
|
+
const dir = new THREE.Vector3()
|
|
354
|
+
this_head.armature.getWorldDirection(dir)
|
|
355
|
+
dir.y = 0
|
|
356
|
+
if (dir.lengthSq() === 0) return
|
|
357
|
+
dir.normalize()
|
|
358
|
+
this_head.armature.position.addScaledVector(dir, this_head.speed * dt / 1000)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// function play_animation(config) {
|
|
362
|
+
// logg(config, 'my play_animation()')
|
|
363
|
+
// let this_head = heads_fn()[config.avatar_id]
|
|
364
|
+
// if (this_head) this_head.playAnimation(geastures[config.animation_name](config))
|
|
365
|
+
// }
|
|
366
|
+
|
|
367
|
+
function cut_to(key) {
|
|
368
|
+
const radio = document.querySelector(`input[name=camera][value="${key}"]`)
|
|
369
|
+
if (radio) radio.checked = true
|
|
370
|
+
setActiveCamera(key, true)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function move_camera(config) {
|
|
374
|
+
const fromKey = String(config.from)
|
|
375
|
+
const toKey = String(config.to)
|
|
376
|
+
const duration = Number(config.duration) > 0 ? Number(config.duration) : CAMERA_BLEND_MS
|
|
377
|
+
const src = cameras_fn(fromKey)
|
|
378
|
+
const dest = cameras_fn(toKey)
|
|
379
|
+
const startTarget = (cameraTargets[fromKey] || cameraTargets.wide).clone()
|
|
380
|
+
const destTarget = (cameraTargets[toKey] || cameraTargets.wide).clone()
|
|
381
|
+
|
|
382
|
+
persistCameraControl(toKey)
|
|
383
|
+
const radio = document.querySelector(`input[name=camera][value="${toKey}"]`)
|
|
384
|
+
if (radio) radio.checked = true
|
|
385
|
+
|
|
386
|
+
;[camera_wide, camera_1, camera_2, camera_3, camera].forEach((cam) => {
|
|
387
|
+
if (cam) {
|
|
388
|
+
cam.aspect = width / height
|
|
389
|
+
cam.updateProjectionMatrix()
|
|
390
|
+
}
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
src.updateMatrixWorld()
|
|
394
|
+
dest.updateMatrixWorld()
|
|
395
|
+
|
|
396
|
+
camera.position.copy(src.position)
|
|
397
|
+
camera.quaternion.copy(src.quaternion)
|
|
398
|
+
camera.fov = src.fov
|
|
399
|
+
camera.near = dest.near
|
|
400
|
+
camera.far = dest.far
|
|
401
|
+
camera.updateProjectionMatrix()
|
|
402
|
+
faceTarget.copy(startTarget)
|
|
403
|
+
if (controls) {
|
|
404
|
+
controls.target.copy(startTarget)
|
|
405
|
+
controls.enabled = false
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
cameraTransition = {
|
|
409
|
+
startPos: src.position.clone(),
|
|
410
|
+
startQuat: src.quaternion.clone(),
|
|
411
|
+
startFov: src.fov,
|
|
412
|
+
startTarget: startTarget,
|
|
413
|
+
endPos: dest.position.clone(),
|
|
414
|
+
endQuat: dest.quaternion.clone(),
|
|
415
|
+
endFov: dest.fov,
|
|
416
|
+
endTarget: destTarget,
|
|
417
|
+
elapsed: 0,
|
|
418
|
+
duration: duration,
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// window.play_animation = play_animation
|
|
423
|
+
window.move_camera = move_camera
|
|
424
|
+
window.cut_to = cut_to
|
|
425
|
+
|
|
426
|
+
let eventsIndex = 0
|
|
427
|
+
function tickEvents(elapsedMs) {
|
|
428
|
+
if (!chunkedInput || !chunkedInput.ftimes || !chunkedInput.fns) return
|
|
429
|
+
while (eventsIndex < chunkedInput.ftimes.length && elapsedMs >= chunkedInput.ftimes[eventsIndex]) {
|
|
430
|
+
const fn = chunkedInput.fns[eventsIndex]
|
|
431
|
+
logg(fn, 'fn')
|
|
432
|
+
eventsIndex++
|
|
433
|
+
eval(fn)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
/*
|
|
440
|
+
**/
|
|
441
|
+
async function init() {
|
|
442
|
+
if (newspartial_id) {
|
|
443
|
+
try {
|
|
444
|
+
const url = wco_origin + '/wco/api/newspartials/' + newspartial_id + '/config.json?api_key=' + api_key + '&api_secret=' + api_secret
|
|
445
|
+
chunkedInput = await fetch(url).then(r => r.json())
|
|
446
|
+
logg(chunkedInput, 'chunkedInput')
|
|
447
|
+
|
|
448
|
+
const last = chunkedInput.wtimes.length-1
|
|
449
|
+
const duration_ms = chunkedInput.wtimes[last] + chunkedInput.wdurations[last]
|
|
450
|
+
totalFrames = duration_ms/1000*fps
|
|
451
|
+
width = chunkedInput.w_px
|
|
452
|
+
height = chunkedInput.h_px
|
|
453
|
+
slug = chunkedInput.slug
|
|
454
|
+
} catch (error) {
|
|
455
|
+
console.log(error)
|
|
456
|
+
chunkedInput = null
|
|
457
|
+
totalFrames = undefined
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
scene = new THREE.Scene()
|
|
462
|
+
scene.background = new THREE.Color(0x333333) // new THREE.Color(0xd3d3d3)
|
|
463
|
+
|
|
464
|
+
/* fov — Camera frustum vertical field of view.
|
|
465
|
+
* aspect — Camera frustum aspect ratio.
|
|
466
|
+
* near — Camera frustum near plane.
|
|
467
|
+
* far — Camera frustum far plane.
|
|
468
|
+
**/
|
|
469
|
+
camera_1 = new THREE.PerspectiveCamera( 10, width/height, 0.1, 1000 )
|
|
470
|
+
camera_1.position.set( 0, 1.6, 4 )
|
|
471
|
+
camera_1.lookAt( 0, 1.5, 0 )
|
|
472
|
+
|
|
473
|
+
camera_2 = new THREE.PerspectiveCamera( 10, width/height, 0.1, 1000 )
|
|
474
|
+
camera_2.position.set( 2, 1.6, 4 )
|
|
475
|
+
camera_2.lookAt( 1, 1.5, 0 )
|
|
476
|
+
|
|
477
|
+
camera_3 = new THREE.PerspectiveCamera( 10, width/height, 0.1, 1000 )
|
|
478
|
+
camera_3.position.set( -2, 1.6, 4 )
|
|
479
|
+
camera_3.lookAt( -1, 1.5, 0 )
|
|
480
|
+
|
|
481
|
+
camera_wide = new THREE.PerspectiveCamera( 25, width/height, 0.1, 1000 )
|
|
482
|
+
camera_wide.position.set( -2, 2.2, 8 )
|
|
483
|
+
camera_wide.lookAt( 0.5, 1.2, 0 )
|
|
484
|
+
|
|
485
|
+
camera = new THREE.PerspectiveCamera( 10, width/height, 0.1, 1000 )
|
|
486
|
+
camera.position.copy( camera_wide.position )
|
|
487
|
+
camera.quaternion.copy( camera_wide.quaternion )
|
|
488
|
+
camera.fov = camera_wide.fov
|
|
489
|
+
camera.updateProjectionMatrix()
|
|
490
|
+
|
|
491
|
+
setup_light(scene)
|
|
492
|
+
|
|
493
|
+
const grid = new THREE.GridHelper(10, 10, 0x888888, 0xbbbbbb)
|
|
494
|
+
grid.position.y = 0
|
|
495
|
+
scene.add(grid)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
let studio
|
|
499
|
+
async function load_studio(s) {
|
|
500
|
+
const cfg = scene_cfg(s)
|
|
501
|
+
if (studio && studio.parent) studio.parent.remove(studio)
|
|
502
|
+
studio = (await gltfLoader.loadAsync(cfg.url)).scene
|
|
503
|
+
rescale(studio, { height: cfg.height })
|
|
504
|
+
set_shadows(studio, false, true)
|
|
505
|
+
scene.add(studio)
|
|
506
|
+
}
|
|
507
|
+
await load_studio(scene_url)
|
|
508
|
+
$('select.studio').on('change', async function() {
|
|
509
|
+
const name = $(this).val()
|
|
510
|
+
if (!scenes[name]) return
|
|
511
|
+
localStorage.setItem(SCENE_STOR, name)
|
|
512
|
+
scene_url = scenes[name]
|
|
513
|
+
loading.textContent = 'Loading...'
|
|
514
|
+
try {
|
|
515
|
+
await load_studio(scene_url)
|
|
516
|
+
loading.textContent = 'loaded'
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.log(error)
|
|
519
|
+
loading.textContent = error.toString()
|
|
520
|
+
}
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
// head = new TalkingHead( document.getElementById('avatar'), {
|
|
525
|
+
// avatarOnly: true,
|
|
526
|
+
// avatarOnlyScene: scene,
|
|
527
|
+
// avatarOnlyCamera: camera,
|
|
528
|
+
// lipsyncModules: ["en"],
|
|
529
|
+
// dracoEnabled: true,
|
|
530
|
+
// })
|
|
531
|
+
head_1 = new TalkingHead( document.getElementById('avatar_1'), {
|
|
532
|
+
avatarOnly: true,
|
|
533
|
+
avatarOnlyScene: scene,
|
|
534
|
+
avatarOnlyCamera: camera_1,
|
|
535
|
+
lipsyncModules: ["en"],
|
|
536
|
+
dracoEnabled: true,
|
|
537
|
+
})
|
|
538
|
+
head_2 = new TalkingHead( document.getElementById('avatar_2'), {
|
|
539
|
+
avatarOnly: true,
|
|
540
|
+
avatarOnlyScene: scene,
|
|
541
|
+
avatarOnlyCamera: camera_1,
|
|
542
|
+
lipsyncModules: ["en"],
|
|
543
|
+
dracoEnabled: true,
|
|
544
|
+
})
|
|
545
|
+
head_3 = new TalkingHead( document.getElementById('avatar_3'), {
|
|
546
|
+
avatarOnly: true,
|
|
547
|
+
avatarOnlyScene: scene,
|
|
548
|
+
avatarOnlyCamera: camera_1,
|
|
549
|
+
lipsyncModules: ["en"],
|
|
550
|
+
dracoEnabled: true,
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
$('select.avatar').on('change', async function() {
|
|
557
|
+
const uid = String($(this).data('uid'))
|
|
558
|
+
const thisHead = heads_fn(uid)
|
|
559
|
+
const val = $(this).val()
|
|
560
|
+
localStorage.setItem(`${AVATAR_STOR}-${uid}`, val || '')
|
|
561
|
+
if (!thisHead) return
|
|
562
|
+
if (thisHead.armature && thisHead.armature.parent) thisHead.armature.parent.remove(thisHead.armature)
|
|
563
|
+
const avatar = avatars[val]
|
|
564
|
+
if (!avatar || !avatar.url) return
|
|
565
|
+
loading.textContent = 'Loading...'
|
|
566
|
+
try {
|
|
567
|
+
if (thisHead.armature && thisHead.armature.parent) thisHead.armature.parent.remove(thisHead.armature)
|
|
568
|
+
await thisHead.showAvatar({
|
|
569
|
+
url: avatar.url,
|
|
570
|
+
body: avatar.type,
|
|
571
|
+
avatarMood: 'neutral',
|
|
572
|
+
lipsyncLang: 'en'
|
|
573
|
+
}, (ev) => {
|
|
574
|
+
if ( ev.lengthComputable ) {
|
|
575
|
+
let val = Math.min(100,Math.round(ev.loaded/ev.total * 100 ))
|
|
576
|
+
loading.textContent = "Loading " + val + "%"
|
|
577
|
+
}
|
|
578
|
+
})
|
|
579
|
+
thisHead.armature.rotation.set(0, 0, 0)
|
|
580
|
+
set_shadows(thisHead.armature, true, false)
|
|
581
|
+
scene.add(thisHead.armature)
|
|
582
|
+
put_feet_at_origin(thisHead)
|
|
583
|
+
thisHead.armature.position.add(new THREE.Vector3(...u_positions[uid]))
|
|
584
|
+
point_camera_at_face(`av_${uid}`, thisHead)
|
|
585
|
+
await thisHead.streamStart(streamOpts, () => {}, () => {}, onSubtitles, onMetrics)
|
|
586
|
+
loading.textContent = 'loaded'
|
|
587
|
+
} catch (error) {
|
|
588
|
+
console.log(error)
|
|
589
|
+
loading.textContent = error.toString()
|
|
590
|
+
}
|
|
591
|
+
})
|
|
592
|
+
$('select.avatar').trigger('change')
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
//
|
|
597
|
+
|
|
598
|
+
renderer = new THREE.WebGLRenderer( {
|
|
599
|
+
alpha: false,
|
|
600
|
+
antialias: true,
|
|
601
|
+
} )
|
|
602
|
+
renderer.setClearColor(0xd3d3d3, 1)
|
|
603
|
+
renderer.setPixelRatio( 1 ); // window.devicePixelRatio )
|
|
604
|
+
renderer.setSize( width, height )
|
|
605
|
+
renderer.toneMapping = THREE.ACESFilmicToneMapping
|
|
606
|
+
renderer.toneMappingExposure = 1
|
|
607
|
+
renderer.outputColorSpace = THREE.SRGBColorSpace
|
|
608
|
+
renderer.shadowMap.enabled = true
|
|
609
|
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
|
610
|
+
document.getElementById('rotatingC').appendChild( renderer.domElement )
|
|
611
|
+
|
|
612
|
+
//
|
|
613
|
+
|
|
614
|
+
controls = new OrbitControls( camera, renderer.domElement )
|
|
615
|
+
controls.enableDamping = true
|
|
616
|
+
controls.enableRotate = true
|
|
617
|
+
controls.enablePan = true
|
|
618
|
+
controls.enableZoom = true
|
|
619
|
+
controls.minDistance = 0.5
|
|
620
|
+
controls.maxDistance = 100
|
|
621
|
+
controls.target.copy(faceTarget)
|
|
622
|
+
controls.autoRotate = false
|
|
623
|
+
controls.update()
|
|
624
|
+
|
|
625
|
+
restoreHeadControl()
|
|
626
|
+
document.querySelectorAll('input[name=head]').forEach((input) => {
|
|
627
|
+
input.addEventListener('change', () => {
|
|
628
|
+
if (!input.checked) return
|
|
629
|
+
setActiveHead(input.value)
|
|
630
|
+
})
|
|
631
|
+
})
|
|
632
|
+
const selectedHead = document.querySelector('input[name=head]:checked')
|
|
633
|
+
setActiveHead(selectedHead ? selectedHead.value : '1')
|
|
634
|
+
|
|
635
|
+
restoreCameraControl()
|
|
636
|
+
document.querySelectorAll('input[name=camera]').forEach((input) => {
|
|
637
|
+
input.addEventListener('change', () => {
|
|
638
|
+
if (!input.checked) return
|
|
639
|
+
setActiveCamera(input.value)
|
|
640
|
+
})
|
|
641
|
+
})
|
|
642
|
+
const selected = document.querySelector('input[name=camera]:checked')
|
|
643
|
+
setActiveCamera(selected ? selected.value : 'wide', true)
|
|
644
|
+
|
|
645
|
+
document.querySelectorAll('input.light-ctrl').forEach((input) => {
|
|
646
|
+
input.addEventListener('change', syncLightsFromControls)
|
|
647
|
+
})
|
|
648
|
+
syncLightsFromControls()
|
|
649
|
+
$('#is_shadows').on('change', sync_shadows)
|
|
650
|
+
sync_shadows()
|
|
651
|
+
|
|
652
|
+
//
|
|
653
|
+
|
|
654
|
+
window.addEventListener( 'resize', onWindowResize )
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
// Keep rendering so OrbitControls drag/damping stay live
|
|
658
|
+
lastAnimTime = performance.now()
|
|
659
|
+
renderer.setAnimationLoop(animate)
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
let semafore = false
|
|
663
|
+
document.addEventListener('DOMContentLoaded', async function(e) {
|
|
664
|
+
if (!semafore) {
|
|
665
|
+
semafore = true
|
|
666
|
+
await init()
|
|
667
|
+
}
|
|
668
|
+
})
|
|
669
|
+
|
|
670
|
+
function cameras_fn(which) {
|
|
671
|
+
switch (which) {
|
|
672
|
+
case 'wide':
|
|
673
|
+
return camera_wide
|
|
674
|
+
case 'av_1':
|
|
675
|
+
return camera_1
|
|
676
|
+
case 'av_2':
|
|
677
|
+
return camera_2
|
|
678
|
+
case 'av_3':
|
|
679
|
+
return camera_3
|
|
680
|
+
default:
|
|
681
|
+
logg('fpq - cameras_fn default - this should never happen')
|
|
682
|
+
return camera_wide
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function heads_fn(which) {
|
|
687
|
+
switch (which) {
|
|
688
|
+
case '1':
|
|
689
|
+
return head_1
|
|
690
|
+
break;
|
|
691
|
+
case '2':
|
|
692
|
+
return head_2
|
|
693
|
+
break;
|
|
694
|
+
case '3':
|
|
695
|
+
return head_3
|
|
696
|
+
break;
|
|
697
|
+
default:
|
|
698
|
+
logg('fpp - heads_fn default - this should never happen')
|
|
699
|
+
return head_1
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function setActiveHead(id) {
|
|
704
|
+
head = heads_fn(id)
|
|
705
|
+
persistHeadControl(id)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function easeInOutCubic(t) {
|
|
709
|
+
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function setActiveCamera(id, instant = false) {
|
|
713
|
+
const key = String(id)
|
|
714
|
+
persistCameraControl(key)
|
|
715
|
+
const dest = cameras_fn(key)
|
|
716
|
+
const destTarget = (cameraTargets[key] || cameraTargets.wide).clone()
|
|
717
|
+
;[camera_wide, camera_1, camera_2, camera_3, camera].forEach((cam) => {
|
|
718
|
+
if (cam) {
|
|
719
|
+
cam.aspect = width / height
|
|
720
|
+
cam.updateProjectionMatrix()
|
|
721
|
+
}
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
dest.updateMatrixWorld()
|
|
725
|
+
|
|
726
|
+
const endPos = dest.position.clone()
|
|
727
|
+
const endQuat = dest.quaternion.clone()
|
|
728
|
+
|
|
729
|
+
if (instant || !controls) {
|
|
730
|
+
camera.position.copy(endPos)
|
|
731
|
+
camera.quaternion.copy(endQuat)
|
|
732
|
+
camera.fov = dest.fov
|
|
733
|
+
camera.near = dest.near
|
|
734
|
+
camera.far = dest.far
|
|
735
|
+
camera.updateProjectionMatrix()
|
|
736
|
+
faceTarget.copy(destTarget)
|
|
737
|
+
if (controls) {
|
|
738
|
+
controls.target.copy(destTarget)
|
|
739
|
+
controls.update()
|
|
740
|
+
}
|
|
741
|
+
cameraTransition = null
|
|
742
|
+
return
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
cameraTransition = {
|
|
746
|
+
startPos: camera.position.clone(),
|
|
747
|
+
startQuat: camera.quaternion.clone(),
|
|
748
|
+
startFov: camera.fov,
|
|
749
|
+
startTarget: controls.target.clone(),
|
|
750
|
+
endPos: endPos,
|
|
751
|
+
endQuat: endQuat,
|
|
752
|
+
endFov: dest.fov,
|
|
753
|
+
endTarget: destTarget,
|
|
754
|
+
elapsed: 0,
|
|
755
|
+
duration: CAMERA_BLEND_MS,
|
|
756
|
+
}
|
|
757
|
+
controls.enabled = false
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function updateCameraTransition(dt) {
|
|
761
|
+
if (!cameraTransition) return
|
|
762
|
+
cameraTransition.elapsed += dt
|
|
763
|
+
const u = Math.min(1, cameraTransition.elapsed / cameraTransition.duration)
|
|
764
|
+
const e = easeInOutCubic(u)
|
|
765
|
+
const tr = cameraTransition
|
|
766
|
+
camera.position.lerpVectors(tr.startPos, tr.endPos, e)
|
|
767
|
+
camera.quaternion.slerpQuaternions(tr.startQuat, tr.endQuat, e)
|
|
768
|
+
camera.fov = tr.startFov + (tr.endFov - tr.startFov) * e
|
|
769
|
+
camera.updateProjectionMatrix()
|
|
770
|
+
if (controls) {
|
|
771
|
+
controls.target.lerpVectors(tr.startTarget, tr.endTarget, e)
|
|
772
|
+
faceTarget.copy(controls.target)
|
|
773
|
+
} else {
|
|
774
|
+
faceTarget.lerpVectors(tr.startTarget, tr.endTarget, e)
|
|
775
|
+
}
|
|
776
|
+
if (u >= 1) {
|
|
777
|
+
cameraTransition = null
|
|
778
|
+
if (!capturing && controls) {
|
|
779
|
+
controls.enabled = true
|
|
780
|
+
controls.update()
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function onWindowResize() {
|
|
786
|
+
;[camera_wide, camera_1, camera_2, camera_3].forEach((cam) => {
|
|
787
|
+
if (cam) {
|
|
788
|
+
cam.aspect = width / height
|
|
789
|
+
cam.updateProjectionMatrix()
|
|
790
|
+
}
|
|
791
|
+
})
|
|
792
|
+
renderer.setSize( width, height )
|
|
793
|
+
render()
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function render() {
|
|
797
|
+
renderer.render( scene, camera )
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function animate() {
|
|
801
|
+
let dt
|
|
802
|
+
if (capturing) {
|
|
803
|
+
dt = 1000 / fps
|
|
804
|
+
} else {
|
|
805
|
+
const now = performance.now()
|
|
806
|
+
dt = lastAnimTime ? (now - lastAnimTime) : (1000 / fps)
|
|
807
|
+
lastAnimTime = now
|
|
808
|
+
if (dt > 100) dt = 100
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (capturing) tickEvents(captureFrame * dt)
|
|
812
|
+
|
|
813
|
+
if (head_1) head_1.animate(dt)
|
|
814
|
+
if (head_2) head_2.animate(dt)
|
|
815
|
+
if (head_3) head_3.animate(dt)
|
|
816
|
+
update_walk(head_1, dt)
|
|
817
|
+
update_walk(head_2, dt)
|
|
818
|
+
update_walk(head_3, dt)
|
|
819
|
+
updateCameraTransition(dt)
|
|
820
|
+
if (!cameraTransition && !capturing) controls.update()
|
|
821
|
+
renderer.render( scene, camera )
|
|
822
|
+
|
|
823
|
+
if (capturing) {
|
|
824
|
+
capturer.capture( renderer.domElement )
|
|
825
|
+
captureFrame++
|
|
826
|
+
if (captureFrame >= captureTotal) {
|
|
827
|
+
finishCapture()
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async function startSpeakCapture() {
|
|
833
|
+
if (capturing) return
|
|
834
|
+
if (!head) return
|
|
835
|
+
if (!chunkedInput) return
|
|
836
|
+
|
|
837
|
+
const last = chunkedInput.wtimes.length - 1
|
|
838
|
+
const duration_ms = chunkedInput.wtimes[last] + chunkedInput.wdurations[last]
|
|
839
|
+
captureTotal = Math.max(1, Math.ceil(duration_ms / 1000 * fps))
|
|
840
|
+
captureFrame = 0
|
|
841
|
+
eventsIndex = 0
|
|
842
|
+
capturing = true
|
|
843
|
+
cameraTransition = null
|
|
844
|
+
if (controls) controls.enabled = false
|
|
845
|
+
|
|
846
|
+
capturer = new CCapture( { format: 'webm', framerate: fps } )
|
|
847
|
+
capturer.start()
|
|
848
|
+
logg('capturer.start')
|
|
849
|
+
status.textContent = 'capturing'
|
|
850
|
+
head.streamAudio(chunkedInput)
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function finishCapture() {
|
|
854
|
+
capturing = false
|
|
855
|
+
capturer.stop()
|
|
856
|
+
logg('capturer.stop')
|
|
857
|
+
lastAnimTime = performance.now()
|
|
858
|
+
if (controls) controls.enabled = true
|
|
859
|
+
|
|
860
|
+
capturer.save((blob) => {
|
|
861
|
+
logg(blob, 'blob')
|
|
862
|
+
renderer.domElement.toBlob((thumb) => {
|
|
863
|
+
logg(thumb, 'thumb')
|
|
864
|
+
const form = new FormData()
|
|
865
|
+
form.append('video', blob, 'lips.webm')
|
|
866
|
+
form.append('name', slug)
|
|
867
|
+
form.append('thumb', thumb)
|
|
868
|
+
if (newspartial_id) form.append('newspartial_id', newspartial_id)
|
|
869
|
+
|
|
870
|
+
const url = wco_origin
|
|
871
|
+
? (wco_origin + '/wco/api/videos/?api_key=' + api_key + '&api_secret=' + api_secret)
|
|
872
|
+
: '/wco/api/videos/'
|
|
873
|
+
fetch(url, {
|
|
874
|
+
method: 'POST',
|
|
875
|
+
body: form,
|
|
876
|
+
}).then(() => {
|
|
877
|
+
status.textContent = 'finished'
|
|
878
|
+
}).catch((error) => {
|
|
879
|
+
console.log(error)
|
|
880
|
+
status.textContent = 'save failed'
|
|
881
|
+
})
|
|
882
|
+
})
|
|
883
|
+
})
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
// Speak when clicked
|
|
888
|
+
$('#speak').on('click', async () => {
|
|
889
|
+
try {
|
|
890
|
+
await startSpeakCapture()
|
|
891
|
+
} catch (error) {
|
|
892
|
+
console.log(error)
|
|
893
|
+
capturing = false
|
|
894
|
+
lastAnimTime = performance.now()
|
|
895
|
+
if (controls) controls.enabled = true
|
|
896
|
+
}
|
|
897
|
+
})
|
|
898
|
+
|
|
899
|
+
$('#jesture').on('click', async () => {
|
|
900
|
+
try {
|
|
901
|
+
if (!head) return
|
|
902
|
+
const name = $('select.gestures').val()
|
|
903
|
+
const g = gestures[name]
|
|
904
|
+
const _anim = gesture_url(g, head.avatar)
|
|
905
|
+
head.speed = (g && typeof g === 'object' && g.speed) ? g.speed : 0
|
|
906
|
+
head.playAnimation( _anim )
|
|
907
|
+
} catch (error) {
|
|
908
|
+
console.log(error)
|
|
909
|
+
}
|
|
910
|
+
})
|
|
911
|
+
|
|
912
|
+
console.log('+++ loaded wco_models :: newsproducer :: studio_blue.js')
|