@realsee/vreo 0.1.0

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.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +465 -0
  3. package/docs/.nojekyll +1 -0
  4. package/docs/assets/custom.css +52 -0
  5. package/docs/assets/highlight.css +127 -0
  6. package/docs/assets/icons.css +1043 -0
  7. package/docs/assets/icons.png +0 -0
  8. package/docs/assets/icons@2x.png +0 -0
  9. package/docs/assets/main.js +52 -0
  10. package/docs/assets/search.js +1 -0
  11. package/docs/assets/style.css +1388 -0
  12. package/docs/assets/widgets.png +0 -0
  13. package/docs/assets/widgets@2x.png +0 -0
  14. package/docs/classes/Player.Player-1.html +50 -0
  15. package/docs/enums/Player.InfoPanelTypeEnum.html +3 -0
  16. package/docs/enums/Player.PanoEffectEnum.html +5 -0
  17. package/docs/enums/Player.PanoTagEnum.html +3 -0
  18. package/docs/enums/Player.VreoKeyframeEnum.html +388 -0
  19. package/docs/enums/fivePlugins.CameraMovementEffect.html +1 -0
  20. package/docs/enums/fivePlugins.Rotation.html +1 -0
  21. package/docs/index.html +204 -0
  22. package/docs/interfaces/Player.PlayerConfigs.html +4 -0
  23. package/docs/interfaces/Player.Vertex.html +3 -0
  24. package/docs/interfaces/Player.VreoKeyframe.html +3 -0
  25. package/docs/interfaces/Player.VreoUnit.html +3 -0
  26. package/docs/interfaces/Player.VreoVideo.html +3 -0
  27. package/docs/interfaces/fivePlugins.CameraMovementOptsCallback.html +1 -0
  28. package/docs/interfaces/fivePlugins.CameraMovementPluginExportType.html +7 -0
  29. package/docs/interfaces/fivePlugins.CameraMovementPluginParameterType.html +1 -0
  30. package/docs/interfaces/fivePlugins.ModelTVVideoPluginData.html +1 -0
  31. package/docs/interfaces/fivePlugins.ModelTVVideoPluginExportType.html +1 -0
  32. package/docs/interfaces/fivePlugins.ModelTVVideoPluginParameterType.html +1 -0
  33. package/docs/interfaces/react.VreoActionCallbacks.html +15 -0
  34. package/docs/modules/Player.html +29 -0
  35. package/docs/modules/fivePlugins.html +3 -0
  36. package/docs/modules/react.html +1 -0
  37. package/docs/modules.html +1 -0
  38. package/lib/Player/App.d.ts +2 -0
  39. package/lib/Player/App.js +68 -0
  40. package/lib/Player/Controller.d.ts +39 -0
  41. package/lib/Player/Controller.js +226 -0
  42. package/lib/Player/hooks.d.ts +4 -0
  43. package/lib/Player/hooks.js +41 -0
  44. package/lib/Player/index.d.ts +15 -0
  45. package/lib/Player/index.js +191 -0
  46. package/lib/Player/modules/Drawer/index.d.ts +2 -0
  47. package/lib/Player/modules/Drawer/index.js +67 -0
  48. package/lib/Player/modules/VideoAgent/VideoAgentMesh.d.ts +30 -0
  49. package/lib/Player/modules/VideoAgent/VideoAgentMesh.js +324 -0
  50. package/lib/Player/modules/VideoAgent/VideoAgentScene.d.ts +10 -0
  51. package/lib/Player/modules/VideoAgent/VideoAgentScene.js +65 -0
  52. package/lib/Player/modules/VideoAgent/index.d.ts +4 -0
  53. package/lib/Player/modules/VideoAgent/index.js +44 -0
  54. package/lib/Player/modules/keyframes/CameraMovement/index.d.ts +2 -0
  55. package/lib/Player/modules/keyframes/CameraMovement/index.js +73 -0
  56. package/lib/Player/modules/keyframes/InfoPanel/index.d.ts +2 -0
  57. package/lib/Player/modules/keyframes/InfoPanel/index.js +89 -0
  58. package/lib/Player/modules/keyframes/ModelVideo/index.d.ts +2 -0
  59. package/lib/Player/modules/keyframes/ModelVideo/index.js +87 -0
  60. package/lib/Player/modules/keyframes/PanoEffect/index.d.ts +2 -0
  61. package/lib/Player/modules/keyframes/PanoEffect/index.js +142 -0
  62. package/lib/Player/modules/keyframes/PanoEffect/lineAnime.d.ts +5 -0
  63. package/lib/Player/modules/keyframes/PanoEffect/lineAnime.js +46 -0
  64. package/lib/Player/modules/keyframes/PanoTag/index.d.ts +2 -0
  65. package/lib/Player/modules/keyframes/PanoTag/index.js +168 -0
  66. package/lib/Player/modules/keyframes/PanoTextLabel/index.d.ts +2 -0
  67. package/lib/Player/modules/keyframes/PanoTextLabel/index.js +113 -0
  68. package/lib/Player/modules/keyframes/Prompter/index.d.ts +2 -0
  69. package/lib/Player/modules/keyframes/Prompter/index.js +105 -0
  70. package/lib/Player/modules/keyframes/UpdateVRPanorama/index.d.ts +2 -0
  71. package/lib/Player/modules/keyframes/UpdateVRPanorama/index.js +51 -0
  72. package/lib/Player/modules/keyframes/VideoEffect/index.d.ts +2 -0
  73. package/lib/Player/modules/keyframes/VideoEffect/index.js +176 -0
  74. package/lib/Player/typings.d.ts +14 -0
  75. package/lib/Player/typings.js +5 -0
  76. package/lib/fivePlugins/CameraMovementPlugin/index.d.ts +6 -0
  77. package/lib/fivePlugins/CameraMovementPlugin/index.js +307 -0
  78. package/lib/fivePlugins/CameraMovementPlugin/typings.d.ts +48 -0
  79. package/lib/fivePlugins/CameraMovementPlugin/typings.js +25 -0
  80. package/lib/fivePlugins/ModelTVVideoPlugin/index.d.ts +20 -0
  81. package/lib/fivePlugins/ModelTVVideoPlugin/index.js +305 -0
  82. package/lib/index.d.ts +1 -0
  83. package/lib/index.js +13 -0
  84. package/lib/react/index.d.ts +42 -0
  85. package/lib/react/index.js +120 -0
  86. package/lib/shared-utils/Preloader.d.ts +6 -0
  87. package/lib/shared-utils/Preloader.js +51 -0
  88. package/lib/shared-utils/animationFrame/BetterTween.d.ts +11 -0
  89. package/lib/shared-utils/animationFrame/BetterTween.js +102 -0
  90. package/lib/shared-utils/animationFrame/index.d.ts +15 -0
  91. package/lib/shared-utils/animationFrame/index.js +68 -0
  92. package/lib/shared-utils/animationFrame/tween.d.ts +75 -0
  93. package/lib/shared-utils/animationFrame/tween.js +76 -0
  94. package/lib/typings/VreoUnit.d.ts +204 -0
  95. package/lib/typings/VreoUnit.js +65 -0
  96. package/package.json +55 -0
  97. package/resources/Player/App.tsx +57 -0
  98. package/resources/Player/Controller.ts +154 -0
  99. package/resources/Player/hooks.ts +28 -0
  100. package/resources/Player/index.tsx +115 -0
  101. package/resources/Player/modules/Drawer/index.tsx +40 -0
  102. package/resources/Player/modules/VideoAgent/VideoAgentMesh.ts +202 -0
  103. package/resources/Player/modules/VideoAgent/VideoAgentScene.ts +38 -0
  104. package/resources/Player/modules/VideoAgent/index.tsx +31 -0
  105. package/resources/Player/modules/keyframes/CameraMovement/index.tsx +33 -0
  106. package/resources/Player/modules/keyframes/InfoPanel/index.tsx +52 -0
  107. package/resources/Player/modules/keyframes/ModelVideo/index.tsx +44 -0
  108. package/resources/Player/modules/keyframes/PanoEffect/index.tsx +99 -0
  109. package/resources/Player/modules/keyframes/PanoEffect/lineAnime.ts +31 -0
  110. package/resources/Player/modules/keyframes/PanoTag/index.tsx +127 -0
  111. package/resources/Player/modules/keyframes/PanoTextLabel/index.tsx +65 -0
  112. package/resources/Player/modules/keyframes/Prompter/index.tsx +65 -0
  113. package/resources/Player/modules/keyframes/UpdateVRPanorama/index.tsx +36 -0
  114. package/resources/Player/modules/keyframes/VideoEffect/index.tsx +115 -0
  115. package/resources/Player/typings.ts +15 -0
  116. package/resources/fivePlugins/CameraMovementPlugin/index.ts +203 -0
  117. package/resources/fivePlugins/CameraMovementPlugin/typings.ts +57 -0
  118. package/resources/fivePlugins/ModelTVVideoPlugin/index.ts +278 -0
  119. package/resources/index.ts +1 -0
  120. package/resources/react/index.tsx +95 -0
  121. package/resources/shared-utils/Preloader.ts +35 -0
  122. package/resources/shared-utils/animationFrame/BetterTween.ts +33 -0
  123. package/resources/shared-utils/animationFrame/index.ts +48 -0
  124. package/resources/shared-utils/animationFrame/tween.ts +51 -0
  125. package/resources/typings/VreoUnit.ts +225 -0
  126. package/stylesheets/default.css +497 -0
@@ -0,0 +1,57 @@
1
+ import * as React from 'react'
2
+ import classNames from 'classnames'
3
+ import { observer } from 'mobx-react'
4
+ import { Controller } from './Controller'
5
+ import { useController } from './hooks'
6
+ import { CameraMovement } from './modules/keyframes/CameraMovement'
7
+ import { InfoPanel } from './modules/keyframes/InfoPanel'
8
+ import { ModelVideo } from './modules/keyframes/ModelVideo'
9
+ import { PanoEffect } from './modules/keyframes/PanoEffect'
10
+ import { PanoTag } from './modules/keyframes/PanoTag'
11
+ import { PanoTextLabel } from './modules/keyframes/PanoTextLabel'
12
+ import { Prompter } from './modules/keyframes/Prompter'
13
+ import { UpdateVRPanorama } from './modules/keyframes/UpdateVRPanorama'
14
+ import { VideoEffect } from './modules/keyframes/VideoEffect'
15
+ import { VideoAgent } from './modules/VideoAgent'
16
+
17
+ const AppView = observer(({ controller }: { controller: Controller }) => {
18
+ return (
19
+ <div
20
+ className={classNames('vreo-panel', {
21
+ 'vreo-panel--hidden': !controller.visible,
22
+ })}
23
+ >
24
+ <div className="vreo-panel-inner">
25
+ <VideoAgent
26
+ onClick={() => {
27
+ if (controller.playing) {
28
+ controller.setPlaying(false)
29
+ } else {
30
+ controller.setPlaying(true)
31
+ }
32
+ }}
33
+ />
34
+ <Prompter />
35
+ </div>
36
+ </div>
37
+ )
38
+ })
39
+
40
+ export function App() {
41
+ const controller = useController()
42
+ return (
43
+ <>
44
+ <>
45
+ <CameraMovement />
46
+ <ModelVideo />
47
+ <UpdateVRPanorama />
48
+ <VideoEffect />
49
+ <PanoTag />
50
+ <PanoTextLabel />
51
+ <InfoPanel />
52
+ <PanoEffect />
53
+ </>
54
+ <AppView controller={controller} />
55
+ </>
56
+ )
57
+ }
@@ -0,0 +1,154 @@
1
+ import { Five, Subscribe } from '@realsee/five'
2
+ import { action, makeObservable, observable, reaction } from 'mobx'
3
+ import * as React from 'react'
4
+ import { requestAnimationFrameInterval } from '../shared-utils/animationFrame'
5
+ import { VreoKeyframe, VreoKeyframeEnum, VreoKeyframeEvent, VreoUnit } from '../typings/VreoUnit'
6
+ import { VideoAgentScene } from './modules/VideoAgent/VideoAgentScene'
7
+ import { PlayerConfigs } from './typings'
8
+
9
+ /**
10
+ * 逻辑控制器:内部状态。
11
+ */
12
+ export class Controller extends Subscribe<VreoKeyframeEvent> {
13
+
14
+ $five?: Five
15
+ configs?: PlayerConfigs
16
+ videoAgentScene?: VideoAgentScene
17
+ vreoUnit?: VreoUnit
18
+ stopInterval?: () => void
19
+ playing = false
20
+
21
+ visible = false
22
+
23
+ drawerConfig?: {
24
+ content: string | JSX.Element
25
+ height?: number | string
26
+ }
27
+
28
+ setVisible(v: boolean) {
29
+ this.visible = v
30
+ }
31
+
32
+ setPlaying(playing: boolean) {
33
+ this.playing = playing
34
+ }
35
+
36
+ openDrawer(drawerConfig?: false | { content: string | JSX.Element; height?: number | string }) {
37
+ if (!drawerConfig) {
38
+ this.drawerConfig = {
39
+ content: '',
40
+ height: this.drawerConfig?.height
41
+ }
42
+ return
43
+ }
44
+
45
+ this.drawerConfig = drawerConfig
46
+ }
47
+
48
+ constructor() {
49
+ super()
50
+
51
+ makeObservable(this, {
52
+ visible: observable,
53
+ setVisible: action,
54
+ playing: observable,
55
+ setPlaying: action,
56
+ drawerConfig: observable.ref,
57
+ openDrawer: action,
58
+
59
+ })
60
+
61
+ // 监听播放情况:抛出触发时机
62
+ reaction(
63
+ () => this.playing,
64
+ (playing) => {
65
+ this.emit(playing ? 'playing' : 'paused')
66
+ }
67
+ )
68
+ }
69
+
70
+ get ready() {
71
+ return !!this.videoAgentScene
72
+ }
73
+
74
+ get currentTime() {
75
+ return this.videoAgentScene?.videoAgentMesh.currentTime || 0
76
+ }
77
+
78
+ get currentKeyframes() {
79
+ if (!this.vreoUnit) return []
80
+ const keyframes = this.vreoUnit.keyframes
81
+ // 没有被解析过且开始时间低于当前时间戳 100ms
82
+ return keyframes.filter((keyframe: VreoKeyframe) => {
83
+ if (keyframe.parsed) return false
84
+ const dur = this.currentTime - keyframe.start
85
+ return dur <= 100 && dur >= 0
86
+ })
87
+ }
88
+
89
+ get videoInstance() {
90
+ return this.videoAgentScene?.videoAgentMesh.videoInstance
91
+ }
92
+
93
+ /**
94
+ * 逐帧任务
95
+ */
96
+ requestAnimationFrameLoop(callback: (type: VreoKeyframeEnum, keyframe: VreoKeyframe) => void) {
97
+ if (this.videoInstance?.ended) {
98
+ this.setPlaying(false)
99
+ this.videoInstance.pause()
100
+ return
101
+ }
102
+ if (!this.playing) {
103
+ if (!this.videoInstance?.paused) {
104
+ this.videoInstance?.pause()
105
+ }
106
+ return
107
+ }
108
+ if (this.videoInstance?.paused && this.playing) {
109
+ this.videoInstance.play()
110
+ }
111
+
112
+ const currentKeyframes = this.currentKeyframes
113
+
114
+ currentKeyframes.forEach((keyframe) => {
115
+ if (keyframe.parsed) return
116
+ keyframe.parsed = true
117
+ this.emit(keyframe.type, keyframe)
118
+ if (callback) {
119
+ callback(keyframe.type, keyframe)
120
+ }
121
+ })
122
+ }
123
+
124
+ run(callback: (type: VreoKeyframeEnum, keyframe: VreoKeyframe) => void) {
125
+ if (this.stopInterval) return
126
+ this.stopInterval = requestAnimationFrameInterval(() => this.requestAnimationFrameLoop(callback))
127
+ }
128
+
129
+ dispose() {
130
+ this.setPlaying(false)
131
+
132
+ if (this.currentKeyframes) {
133
+ this.currentKeyframes.forEach((keyframe) => (keyframe.parsed = false))
134
+ }
135
+
136
+ /**
137
+ * 清理掉数据
138
+ */
139
+ this.vreoUnit = undefined
140
+ if (this.videoInstance) {
141
+ this.videoInstance.currentTime = 0
142
+ }
143
+
144
+ if (this.stopInterval) {
145
+ this.stopInterval()
146
+ this.stopInterval = undefined
147
+ }
148
+ }
149
+ }
150
+
151
+ export const ControllerContext = React.createContext<Controller | null>(null)
152
+
153
+
154
+
@@ -0,0 +1,28 @@
1
+ import React from 'react'
2
+ import { Vector3 } from 'three'
3
+ import { ControllerContext } from './Controller'
4
+
5
+
6
+
7
+ export function useController() {
8
+ const controller = React.useContext(ControllerContext)
9
+ if (!controller) {
10
+ throw new Error('没有找到 "ControllerContext"')
11
+ }
12
+
13
+ return controller
14
+ }
15
+
16
+ export function useFiveInstance() {
17
+ const controller = useController()
18
+ if (!controller.$five) {
19
+ throw new Error('没有找到 "five" 实例')
20
+ }
21
+
22
+ return controller.$five
23
+ }
24
+
25
+ export function useFiveProject2d() {
26
+ const five = useFiveInstance()
27
+ return (vector: Vector3) => five.project2d(vector, false)
28
+ }
@@ -0,0 +1,115 @@
1
+ import * as React from 'react'
2
+ import * as ReactDOM from 'react-dom'
3
+ import { Five, Subscribe } from '@realsee/five'
4
+
5
+ import { App } from './App'
6
+ import { Controller, ControllerContext } from './Controller'
7
+ import { VreoKeyframeEvent, VreoKeyframeConfigMap, VreoUnit } from '../typings/VreoUnit'
8
+ import { reaction } from 'mobx'
9
+ import { Drawer } from './modules/Drawer'
10
+ import { PlayerConfigs } from './typings'
11
+
12
+ const controller = new Controller()
13
+
14
+ export class Player extends Subscribe<VreoKeyframeEvent> {
15
+ $five: Five
16
+ configs: Readonly<PlayerConfigs>
17
+
18
+ constructor(five: Five, configs: Partial<PlayerConfigs> = {}) {
19
+ super()
20
+ this.$five = controller.$five = five
21
+
22
+ if (!configs.containter) {
23
+ const containter = document.createElement('div')
24
+ containter.setAttribute('id', 'vreo-app')
25
+ configs.containter = containter
26
+ document.body.append(containter)
27
+ }
28
+
29
+ this.configs = Object.freeze(
30
+ Object.assign(
31
+ {
32
+ keyframeMap: {},
33
+ },
34
+ configs
35
+ )
36
+ )
37
+
38
+ controller.configs = this.configs
39
+
40
+ ReactDOM.render(
41
+ <ControllerContext.Provider value={controller}>
42
+ <App></App>
43
+ <Drawer />
44
+ </ControllerContext.Provider>,
45
+ configs.containter
46
+ )
47
+
48
+ // 监听播放情况:抛出触发时机
49
+ reaction(
50
+ () => controller.playing,
51
+ (playing) => {
52
+ this.emit(playing ? 'playing' : 'paused')
53
+ }
54
+ )
55
+ }
56
+
57
+ async load(vreoUnit: VreoUnit, currentTime = 0) {
58
+ if (controller.stopInterval) {
59
+ controller.stopInterval()
60
+ controller.stopInterval = undefined
61
+ }
62
+
63
+ controller.vreoUnit = vreoUnit
64
+ controller.videoInstance?.pause()
65
+
66
+ if (vreoUnit.video.url) {
67
+ if (controller.videoAgentScene?.videoAgentMesh.videoInstance) {
68
+ controller.videoAgentScene.videoAgentMesh.videoInstance.currentTime = currentTime / 1000
69
+ }
70
+ await controller.videoAgentScene?.videoAgentMesh.play(vreoUnit.video.url)
71
+ this.play()
72
+ }
73
+ controller.run((type, keyframe) => this.emit(type, keyframe))
74
+ return true
75
+ }
76
+
77
+ get paused() {
78
+ return !controller.playing
79
+ }
80
+
81
+ play() {
82
+ if (controller.playing) return true
83
+ controller.setPlaying(true)
84
+ return true
85
+ }
86
+
87
+ pause() {
88
+ controller.setPlaying(false)
89
+ }
90
+
91
+ show() {
92
+ controller.setVisible(true)
93
+ }
94
+
95
+ hide() {
96
+ controller.setVisible(false)
97
+ }
98
+
99
+ dispose() {
100
+ controller.dispose()
101
+ }
102
+ }
103
+
104
+ console.log(`
105
+
106
+ ┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━
107
+ ┃┏━┓┃━━━━━━━━━┃┃━━━━━━━━━━━━━
108
+ ┃┗━┛┃┏━━┓┏━━┓━┃┃━┏━━┓┏━━┓┏━━┓
109
+ ┃┏┓┏┛┃┏┓┃┗━┓┃━┃┃━┃━━┫┃┏┓┃┃┏┓┃
110
+ ┃┃┃┗┓┃┃━┫┃┗┛┗┓┃┗┓┣━━┃┃┃━┫┃┃━┫
111
+ ┗┛┗━┛┗━━┛┗━━━┛┗━┛┗━━┛┗━━┛┗━━┛
112
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
113
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
114
+
115
+ `)
@@ -0,0 +1,40 @@
1
+ import * as React from 'react'
2
+ import classNames from 'classnames'
3
+ import { observer } from 'mobx-react'
4
+ import { Controller } from '../../Controller'
5
+ import { useController } from '../../hooks'
6
+
7
+ const DrawerView = observer(({ controller }: { controller: Controller }) => {
8
+ const maxHeight = '540px'
9
+ const height = (() => {
10
+ const height = controller.drawerConfig?.height
11
+ if (!height) {
12
+ return 'max-content'
13
+ }
14
+
15
+ if (typeof height === 'number') {
16
+ return height + 'px'
17
+ }
18
+
19
+ return height
20
+ })()
21
+
22
+ return (
23
+ <div
24
+ className={classNames('vreo-drawer', {
25
+ 'vreo-drawer-visible': controller.drawerConfig && controller.drawerConfig.content,
26
+ })}
27
+ onClick={() => controller.openDrawer(false)}
28
+ >
29
+ <div className="vreo-drawer-inner" style={{ height, maxHeight }}>
30
+ {controller.drawerConfig?.content || ''}
31
+ </div>
32
+ </div>
33
+ )
34
+ })
35
+
36
+ export function Drawer() {
37
+ const controller = useController()
38
+
39
+ return <DrawerView controller={controller} />
40
+ }
@@ -0,0 +1,202 @@
1
+ import * as THREE from 'three'
2
+ import { Preloader } from '../../../shared-utils/Preloader'
3
+ import { makeObservable, observable, runInAction } from 'mobx'
4
+
5
+ const vertexShader = `
6
+ varying vec2 vUv;
7
+ varying vec2 vColorUv;
8
+ varying vec2 vMaskUv;
9
+ void main(){
10
+ vUv = uv;
11
+ vColorUv = vec2(uv.x / 2.0, uv.y);
12
+ vMaskUv = vec2(0.5 + uv.x / 2.0, uv.y);
13
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
14
+ }
15
+ `
16
+ const fragmentShaderTpl = () => `
17
+ uniform int enable;
18
+ uniform sampler2D map;
19
+ varying vec2 vUv;
20
+ varying vec2 vColorUv;
21
+ varying vec2 vMaskUv;
22
+
23
+ vec3 hsv2rgb(vec3 c) {
24
+ const vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
25
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
26
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
27
+ }
28
+
29
+ vec3 rgb2hsv(vec3 c) {
30
+ const vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
31
+ vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
32
+ vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
33
+
34
+ float d = q.x - min(q.w, q.y);
35
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + 0.001)), d / (q.x + 0.001), q.x);
36
+ }
37
+
38
+ void main(void) {
39
+ vec3 color = texture2D(map, vUv).xyz;
40
+ float amask = 1.0;
41
+
42
+ float ref = color.r;
43
+ if (ref < color.b) ref = color.b;
44
+ amask = color.g - ref + 0.1;
45
+ amask = smoothstep(0.1, 0.3, amask);
46
+ amask = 1.0 - amask;
47
+
48
+ vec3 target = rgb2hsv(vec3(0.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0));
49
+ vec3 hsv = rgb2hsv(color);
50
+ float distance = abs(hsv.x - target.x);
51
+ if (distance < 0.15) {
52
+ hsv.y = 0.0;
53
+ }
54
+ color = hsv2rgb(hsv);
55
+
56
+ gl_FragColor = vec4(color.rgb, amask * float(enable));
57
+ }
58
+ `
59
+
60
+ export interface VideoAgentMeshOptions {
61
+ videoInstance?: HTMLVideoElement
62
+ }
63
+
64
+ /**
65
+ * 视频经纪人MESH
66
+ */
67
+ export class VideoAgentMesh extends THREE.Mesh {
68
+ options: VideoAgentMeshOptions
69
+ videoUrl?: string
70
+ freeze: boolean
71
+ paused: boolean
72
+ audioInstance: HTMLAudioElement
73
+
74
+ get videoInstance(): HTMLAudioElement {
75
+ if (this.videoUrl?.endsWith('mp3')) {
76
+ return this.audioInstance
77
+ }
78
+ const uniforms = (this.material as THREE.ShaderMaterial).uniforms
79
+ const videoInstance = uniforms.map.value.image as HTMLVideoElement
80
+ return videoInstance
81
+ }
82
+
83
+ /**
84
+ *
85
+ * @param width
86
+ * @param height
87
+ * @param widthSegments
88
+ * @param heightSegments
89
+ * @param options
90
+ */
91
+ constructor(
92
+ width: number,
93
+ height: number,
94
+ widthSegments: number,
95
+ heightSegments: number,
96
+ options: VideoAgentMeshOptions = {},
97
+ ) {
98
+ if (!options.videoInstance) {
99
+ const videoInstance = document.createElement('video')
100
+ videoInstance.style.opacity = '0'
101
+ videoInstance.style.display = 'none'
102
+ document.body.append(videoInstance)
103
+ options.videoInstance = videoInstance
104
+ videoInstance.playsInline = true
105
+ videoInstance.controls = false
106
+ }
107
+
108
+ const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
109
+ const videoTexture = new THREE.VideoTexture(options.videoInstance)
110
+ const material = new THREE.ShaderMaterial({
111
+ uniforms: {
112
+ map: { value: videoTexture },
113
+ enable: { value: 0 },
114
+ },
115
+ vertexShader,
116
+ fragmentShader: fragmentShaderTpl(),
117
+ transparent: true,
118
+ })
119
+
120
+ super(geometry, material)
121
+ this.options = options
122
+ this.freeze = false
123
+ this.paused = true
124
+
125
+ {
126
+ const audioInstance = document.createElement('audio')
127
+ audioInstance.crossOrigin = ''
128
+ // videoInstance.muted = true
129
+ audioInstance.muted = false
130
+ audioInstance.setAttribute('playsinline', 'true')
131
+ audioInstance.setAttribute('webkit-playsinline', 'true')
132
+ audioInstance.setAttribute('autoplay', 'false')
133
+ audioInstance.setAttribute('style', 'display: none;')
134
+ document.body.appendChild(audioInstance)
135
+ this.audioInstance = audioInstance
136
+ }
137
+ makeObservable(this, { paused: observable })
138
+
139
+ const updatePaused = (paused: boolean) => runInAction(() => (this.paused = paused))
140
+ this.videoInstance.addEventListener('pause', () => updatePaused(true))
141
+ this.videoInstance.addEventListener('play', () => updatePaused(false))
142
+ }
143
+
144
+ private async update(videoUrl: string) {
145
+ if (this.videoUrl === videoUrl) {
146
+ return
147
+ }
148
+
149
+ this.videoUrl = videoUrl
150
+ this.freeze = true
151
+ await this.videoInstance.pause()
152
+
153
+ const uniforms = (this.material as THREE.ShaderMaterial).uniforms
154
+ this.videoInstance.muted = true
155
+ this.videoInstance.src = await URL.createObjectURL((await Preloader.blob(this.videoUrl)) as unknown as Blob)
156
+ this.videoInstance.setAttribute('data-src', this.videoUrl)
157
+
158
+ const onStart = () => {
159
+ if (this.videoInstance.currentTime === 0) return
160
+ this.freeze = false
161
+ this.videoInstance.muted = false
162
+ uniforms.enable.value = 1
163
+ this.videoInstance.removeEventListener('timeupdate', onStart, false)
164
+ }
165
+
166
+ this.videoInstance.addEventListener('timeupdate', onStart, false)
167
+ }
168
+
169
+ async play(videoUrl = '') {
170
+ videoUrl = (videoUrl || '').replace(/https*:/, '')
171
+ if (!videoUrl) {
172
+ if (this.videoUrl) await this.videoInstance.play()
173
+ else console.warn('警告:视频资源未初始化。')
174
+ return true
175
+ }
176
+
177
+ if (videoUrl === this.videoUrl) {
178
+ this.videoInstance.currentTime = 0
179
+ await this.videoInstance.play()
180
+ return true
181
+ }
182
+
183
+ await this.update(videoUrl)
184
+
185
+ this.videoInstance.pause()
186
+
187
+ return await new Promise((resolve) =>
188
+ setTimeout(async () => {
189
+ this.videoInstance.currentTime = 0
190
+ await this.videoInstance.play()
191
+ resolve(true)
192
+ }, 20),
193
+ )
194
+ }
195
+
196
+ /**
197
+ * 转成毫秒,保障精准度
198
+ */
199
+ get currentTime() {
200
+ return this.videoInstance.currentTime * 1000
201
+ }
202
+ }
@@ -0,0 +1,38 @@
1
+ import * as THREE from 'three'
2
+ import { VideoAgentMesh } from './VideoAgentMesh'
3
+
4
+ export class VideoAgentScene {
5
+ videoAgentMesh: VideoAgentMesh
6
+
7
+ scene = new THREE.Scene()
8
+ camera = new THREE.OrthographicCamera(-240, 240, 135, -135)
9
+ renderer = new THREE.WebGLRenderer({ alpha: true })
10
+
11
+ constructor(container: HTMLElement, needRender = true) {
12
+ this.camera.position.set(0, 0, 10)
13
+ this.camera.lookAt(0, 0, 0)
14
+ const state = {
15
+ width: container.offsetWidth,
16
+ height: container.offsetHeight,
17
+ }
18
+ const domElement = this.renderer.domElement
19
+ domElement.classList.add('VideoAgent-canvas')
20
+ this.renderer.setSize(state.width * window.devicePixelRatio, state.height * window.devicePixelRatio)
21
+ domElement.setAttribute('style', `width: ${state.width}px;height: ${state.height}px;`)
22
+
23
+ this.videoAgentMesh = new VideoAgentMesh(480, 270, 1, 1)
24
+ if (needRender) {
25
+ container.append(domElement)
26
+ this.scene.add(this.videoAgentMesh)
27
+ this.run()
28
+ }
29
+ }
30
+
31
+ run = () => {
32
+ // 视频开始 1s 不渲染:规避某些设备黑屏闪烁问题。视频前 2s 是最好是静默的。
33
+ if (!(this.videoAgentMesh.paused || this.videoAgentMesh.freeze) && this.videoAgentMesh.currentTime > 1000) {
34
+ this.renderer.render(this.scene, this.camera)
35
+ }
36
+ requestAnimationFrame(this.run)
37
+ }
38
+ }
@@ -0,0 +1,31 @@
1
+ import * as React from 'react'
2
+ import { useController } from '../../hooks'
3
+ import { VideoAgentScene } from './VideoAgentScene'
4
+
5
+ export function VideoAgent(props: { onClick?: () => void }) {
6
+ const ref = React.useRef<HTMLDivElement>(null)
7
+ const controller = useController()
8
+
9
+ React.useEffect(() => {
10
+ if (!ref.current) throw new Error('React 渲染异常,请稍后重试 ...')
11
+ if (controller.videoAgentScene) {
12
+ console.warn('"VideoAgentScene" 重复初始化,已被过滤 ...')
13
+ return
14
+ }
15
+ const videoAgentScene = new VideoAgentScene(ref.current)
16
+ controller.videoAgentScene = videoAgentScene
17
+ }, [])
18
+
19
+ return (
20
+ <div
21
+ className="VideoAgent"
22
+ onClick={() => {
23
+ if (!props.onClick) return
24
+ props.onClick()
25
+ }}
26
+ ref={ref}
27
+ >
28
+ <div className="VideoAgent-play"></div>
29
+ </div>
30
+ )
31
+ }
@@ -0,0 +1,33 @@
1
+ import * as React from 'react'
2
+ import { CameraMovementPlugin } from '../../../../fivePlugins/CameraMovementPlugin'
3
+ import { CameraMovementEffect, RotateArgs } from '../../../../fivePlugins/CameraMovementPlugin/typings'
4
+ import { CameraMovementData, VreoKeyframe, VreoKeyframeEnum } from '../../../../typings/VreoUnit'
5
+ import { useController, useFiveInstance } from '../../../hooks'
6
+
7
+ export function CameraMovement() {
8
+ const controller = useController()
9
+ const five = useFiveInstance()
10
+ const ref = React.useRef<ReturnType<typeof CameraMovementPlugin>>()
11
+
12
+ React.useEffect(() => {
13
+ const callback = async (keyframe: VreoKeyframe) => {
14
+ if (!ref.current) {
15
+ ref.current = CameraMovementPlugin(five, {})
16
+ }
17
+ const { start, end, data } = keyframe
18
+ const cameraMovementData = data as CameraMovementData
19
+ const effect = cameraMovementData.effect
20
+ if (effect === CameraMovementEffect.Rotate) {
21
+ ref.current.rotate(data as RotateArgs, end - start)
22
+ } else {
23
+ ref.current.move(data, end - start)
24
+ }
25
+ }
26
+
27
+ controller.on(VreoKeyframeEnum.CameraMovement, callback)
28
+ return () => {
29
+ controller.off(VreoKeyframeEnum.CameraMovement, callback)
30
+ }
31
+ }, [controller])
32
+ return <></>
33
+ }