@damienmortini/three 0.1.131
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/LICENSE +5 -0
- package/copyExamples.js +15 -0
- package/ecs/THREEView.js +31 -0
- package/examples/loaders/BasisTextureLoader.js +783 -0
- package/examples/loaders/DRACOLoader.js +587 -0
- package/examples/loaders/GLTFLoader.js +4237 -0
- package/examples/objects/Lensflare.js +389 -0
- package/examples/utils/BufferGeometryUtils.js +943 -0
- package/gpgpu/THREEGPGPUSystem.js +175 -0
- package/loader/THREELoader.js +112 -0
- package/loader/meshoptimizerdecoder/THREE.EXT_meshopt_compression.js +41 -0
- package/loader/meshoptimizerdecoder/meshopt_decoder.js +129 -0
- package/material/THREEBaseMaterial.js +68 -0
- package/material/THREEPBRMaterial.js +107 -0
- package/material/THREEShaderMaterial.js +95 -0
- package/object/THREELine.js +113 -0
- package/object/THREEMotionVectorObject.js +284 -0
- package/object/THREERibbon.js +49 -0
- package/object/THREESky.js +279 -0
- package/object/THREESprite.js +96 -0
- package/object/THREESpriteAnimation.js +103 -0
- package/object/THREEText.js +240 -0
- package/package.json +33 -0
- package/renderer/THREERenderer.js +121 -0
- package/renderer/THREEStereoRenderer.js +60 -0
- package/renderer/THREEWebGLRenderTarget2D.js +63 -0
- package/shader/THREEBaseShader.js +33 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { BackSide } from '../../../three/src/constants.js'
|
|
2
|
+
import { Mesh } from '../../../three/src/objects/Mesh.js'
|
|
3
|
+
import { Vector3 } from '../../../three/src/math/Vector3.js'
|
|
4
|
+
import { IcosahedronBufferGeometry } from '../../../three/src/geometries/IcosahedronGeometry.js'
|
|
5
|
+
import THREEShaderMaterial from '../material/THREEShaderMaterial.js'
|
|
6
|
+
import SkyShader from '../../core/shader/SkyShader.js'
|
|
7
|
+
import GradientNoiseShader from '../../core/shader/noise/GradientNoiseShader.js'
|
|
8
|
+
|
|
9
|
+
const skyShader = {
|
|
10
|
+
uniforms: {
|
|
11
|
+
sunPosition: new Vector3(0, 1, 0),
|
|
12
|
+
sunRayleigh: 1.5,
|
|
13
|
+
sunTurbidity: 6,
|
|
14
|
+
sunLuminance: 1,
|
|
15
|
+
sunMieCoefficient: 0.005,
|
|
16
|
+
sunMieDirectionalG: 0.8,
|
|
17
|
+
moonPosition: new Vector3(0, 1, 0),
|
|
18
|
+
moonRayleigh: 0,
|
|
19
|
+
moonTurbidity: 1.5,
|
|
20
|
+
moonLuminance: 1.1,
|
|
21
|
+
moonMieCoefficient: 0.005,
|
|
22
|
+
moonMieDirectionalG: 0.8,
|
|
23
|
+
displaySun: 1,
|
|
24
|
+
displayMoon: 1,
|
|
25
|
+
},
|
|
26
|
+
vertexShaderChunks: [
|
|
27
|
+
['start', `
|
|
28
|
+
varying vec3 vWorldPosition;
|
|
29
|
+
`],
|
|
30
|
+
['end', `
|
|
31
|
+
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
|
|
32
|
+
vWorldPosition = worldPosition.xyz;
|
|
33
|
+
|
|
34
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
35
|
+
`],
|
|
36
|
+
],
|
|
37
|
+
fragmentShaderChunks: [
|
|
38
|
+
['start', `
|
|
39
|
+
uniform vec3 sunPosition;
|
|
40
|
+
uniform float sunRayleigh;
|
|
41
|
+
uniform float sunTurbidity;
|
|
42
|
+
uniform float sunLuminance;
|
|
43
|
+
uniform float sunMieCoefficient;
|
|
44
|
+
uniform float sunMieDirectionalG;
|
|
45
|
+
uniform float displaySun;
|
|
46
|
+
uniform float displayMoon;
|
|
47
|
+
|
|
48
|
+
uniform vec3 moonPosition;
|
|
49
|
+
uniform float moonRayleigh;
|
|
50
|
+
uniform float moonTurbidity;
|
|
51
|
+
uniform float moonLuminance;
|
|
52
|
+
uniform float moonMieCoefficient;
|
|
53
|
+
uniform float moonMieDirectionalG;
|
|
54
|
+
|
|
55
|
+
varying vec3 vWorldPosition;
|
|
56
|
+
|
|
57
|
+
${SkyShader.computeSkyColor()}
|
|
58
|
+
|
|
59
|
+
${GradientNoiseShader.gradientNoise3D()}
|
|
60
|
+
|
|
61
|
+
float blendScreen(float base, float blend) {
|
|
62
|
+
return 1.0-((1.0-base)*(1.0-blend));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
vec3 blendScreen(vec3 base, vec3 blend) {
|
|
66
|
+
return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));
|
|
67
|
+
}
|
|
68
|
+
`],
|
|
69
|
+
['end', `
|
|
70
|
+
vec3 normalizedSunPosition = normalize(sunPosition);
|
|
71
|
+
vec3 normalizedMoonPosition = normalize(moonPosition);
|
|
72
|
+
|
|
73
|
+
vec4 skySunColor = computeSkyColor(vWorldPosition, normalizedSunPosition, ${SkyShader.SUN_ANGULAR_DIAMETER} * displaySun, sunRayleigh, sunTurbidity, sunLuminance, sunMieCoefficient, sunMieDirectionalG);
|
|
74
|
+
vec4 skyMoonColor = computeSkyColor(vWorldPosition, normalizedMoonPosition, ${SkyShader.MOON_ANGULAR_DIAMETER} * displayMoon, moonRayleigh, moonTurbidity, moonLuminance, moonMieCoefficient, moonMieDirectionalG);
|
|
75
|
+
|
|
76
|
+
float nightIntensity = 1. - smoothstep(-.05, 0., normalizedSunPosition.y);
|
|
77
|
+
|
|
78
|
+
float moonIntensity = max(0., -dot(normalize(sunPosition.xz), normalize(moonPosition.xz)));
|
|
79
|
+
// skyMoonColor *= moonIntensity;
|
|
80
|
+
skyMoonColor *= nightIntensity;
|
|
81
|
+
|
|
82
|
+
// Stars
|
|
83
|
+
vec3 rayDirection = normalize(vWorldPosition);
|
|
84
|
+
float starsIntensity = gradientNoise3D(rayDirection * 400.) * .5 + .5;
|
|
85
|
+
starsIntensity = pow(starsIntensity, 15.);
|
|
86
|
+
starsIntensity *= max(0., rayDirection.y);
|
|
87
|
+
starsIntensity *= 10.;
|
|
88
|
+
starsIntensity *= nightIntensity;
|
|
89
|
+
|
|
90
|
+
skyMoonColor.rgb = blendScreen(skyMoonColor.rgb, vec3(starsIntensity));
|
|
91
|
+
|
|
92
|
+
vec3 skyColor = blendScreen(skySunColor.rgb, skyMoonColor.rgb);
|
|
93
|
+
|
|
94
|
+
// gl_FragColor = vec4(skyColor, (skySunColor.a + skyMoonColor.a) / 2.);
|
|
95
|
+
gl_FragColor = vec4(skyColor, 1.);
|
|
96
|
+
`],
|
|
97
|
+
],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default class Sky extends Mesh {
|
|
101
|
+
constructor({
|
|
102
|
+
radius = 1,
|
|
103
|
+
shaders = [],
|
|
104
|
+
} = {}) {
|
|
105
|
+
super(new IcosahedronBufferGeometry(radius, 3), new THREEShaderMaterial(Object.assign({
|
|
106
|
+
type: 'basic',
|
|
107
|
+
side: BackSide,
|
|
108
|
+
shaders,
|
|
109
|
+
}, skyShader)))
|
|
110
|
+
|
|
111
|
+
this._radius = radius
|
|
112
|
+
|
|
113
|
+
this.sunInclination = Math.PI * .5
|
|
114
|
+
this.sunAzimuth = 0
|
|
115
|
+
|
|
116
|
+
this.moonInclination = Math.PI * .5
|
|
117
|
+
this.moonAzimuth = Math.PI
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get radius() {
|
|
121
|
+
return this._radius
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
_updatePositionFromInclinationAzimuth(position, inclination, azimuth) {
|
|
125
|
+
const theta = inclination
|
|
126
|
+
const phi = azimuth + Math.PI * .5
|
|
127
|
+
position.x = this._radius * Math.cos(phi) * Math.cos(theta)
|
|
128
|
+
position.y = this._radius * Math.sin(theta)
|
|
129
|
+
position.z = this._radius * Math.sin(phi) * Math.cos(theta)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get displaySun() {
|
|
133
|
+
return this.material.displaySun === 1
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
set displaySun(value) {
|
|
137
|
+
this.material.displaySun = value ? 1 : 0
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
get displayMoon() {
|
|
141
|
+
return this.material.displayMoon === 1
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
set displayMoon(value) {
|
|
145
|
+
this.material.displayMoon = value ? 1 : 0
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get sunInclination() {
|
|
149
|
+
return this._sunInclination
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
set sunInclination(value) {
|
|
153
|
+
this._sunInclination = value
|
|
154
|
+
this._updatePositionFromInclinationAzimuth(this.sunPosition, this.sunInclination, this.sunAzimuth)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get sunAzimuth() {
|
|
158
|
+
return this._sunAzimuth
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
set sunAzimuth(value) {
|
|
162
|
+
this._sunAzimuth = value
|
|
163
|
+
this._updatePositionFromInclinationAzimuth(this.sunPosition, this.sunInclination, this.sunAzimuth)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get sunPosition() {
|
|
167
|
+
return this.material.sunPosition
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
set sunPosition(value) {
|
|
171
|
+
this.material.sunPosition = value
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get sunRayleigh() {
|
|
175
|
+
return this.material.sunRayleigh
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
set sunRayleigh(value) {
|
|
179
|
+
this.material.sunRayleigh = value
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
get sunTurbidity() {
|
|
183
|
+
return this.material.sunTurbidity
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
set sunTurbidity(value) {
|
|
187
|
+
this.material.sunTurbidity = value
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
get sunLuminance() {
|
|
191
|
+
return this.material.sunLuminance
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
set sunLuminance(value) {
|
|
195
|
+
this.material.sunLuminance = value
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
get sunMieCoefficient() {
|
|
199
|
+
return this.material.sunMieCoefficient
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
set sunMieCoefficient(value) {
|
|
203
|
+
this.material.sunMieCoefficient = value
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get sunMieDirectionalG() {
|
|
207
|
+
return this.material.sunMieDirectionalG
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
set sunMieDirectionalG(value) {
|
|
211
|
+
this.material.sunMieDirectionalG = value
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
get moonInclination() {
|
|
215
|
+
return this._moonInclination
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
set moonInclination(value) {
|
|
219
|
+
this._moonInclination = value
|
|
220
|
+
this._updatePositionFromInclinationAzimuth(this.moonPosition, this.moonInclination, this.moonAzimuth)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
get moonAzimuth() {
|
|
224
|
+
return this._moonAzimuth
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
set moonAzimuth(value) {
|
|
228
|
+
this._moonAzimuth = value
|
|
229
|
+
this._updatePositionFromInclinationAzimuth(this.moonPosition, this.moonInclination, this.moonAzimuth)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
get moonPosition() {
|
|
233
|
+
return this.material.moonPosition
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
set moonPosition(value) {
|
|
237
|
+
this.material.moonPosition = value
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
get moonRayleigh() {
|
|
241
|
+
return this.material.moonRayleigh
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
set moonRayleigh(value) {
|
|
245
|
+
this.material.moonRayleigh = value
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
get moonTurbidity() {
|
|
249
|
+
return this.material.moonTurbidity
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
set moonTurbidity(value) {
|
|
253
|
+
this.material.moonTurbidity = value
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
get moonLuminance() {
|
|
257
|
+
return this.material.moonLuminance
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
set moonLuminance(value) {
|
|
261
|
+
this.material.moonLuminance = value
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
get moonMieCoefficient() {
|
|
265
|
+
return this.material.moonMieCoefficient
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
set moonMieCoefficient(value) {
|
|
269
|
+
this.material.moonMieCoefficient = value
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
get moonMieDirectionalG() {
|
|
273
|
+
return this.material.moonMieDirectionalG
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
set moonMieDirectionalG(value) {
|
|
277
|
+
this.material.moonMieDirectionalG = value
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Object3D, Mesh, Color, PlaneGeometry, Texture, DoubleSide, LinearFilter } from '../../../three/build/three.module.js'
|
|
2
|
+
|
|
3
|
+
import THREEShaderMaterial from './THREEShaderMaterial.js'
|
|
4
|
+
|
|
5
|
+
const CACHED_IMAGES = new Map()
|
|
6
|
+
|
|
7
|
+
export default class Sprite extends Object3D {
|
|
8
|
+
constructor(image, { data, frame, scale = 1 } = {}) {
|
|
9
|
+
super()
|
|
10
|
+
|
|
11
|
+
this._data = data
|
|
12
|
+
this._image = image
|
|
13
|
+
this._scale = scale
|
|
14
|
+
|
|
15
|
+
// Optimise images decoding
|
|
16
|
+
let canvas = CACHED_IMAGES.get(this.image)
|
|
17
|
+
|
|
18
|
+
if (!canvas) {
|
|
19
|
+
canvas = document.createElement('canvas')
|
|
20
|
+
canvas.width = this.image.width
|
|
21
|
+
canvas.height = this.image.height
|
|
22
|
+
const context = canvas.getContext('2d')
|
|
23
|
+
context.drawImage(this.image, 0, 0)
|
|
24
|
+
CACHED_IMAGES.set(this.image, canvas)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.mesh = new Mesh(new PlaneGeometry(1, 1), new THREEShaderMaterial({
|
|
28
|
+
type: 'basic',
|
|
29
|
+
transparent: true,
|
|
30
|
+
side: DoubleSide,
|
|
31
|
+
uniforms: {
|
|
32
|
+
diffuse: new Color(0xffffff),
|
|
33
|
+
map: new Texture(canvas),
|
|
34
|
+
},
|
|
35
|
+
}))
|
|
36
|
+
this.mesh.scale.x = canvas.width * this._scale
|
|
37
|
+
this.mesh.scale.y = canvas.height * this._scale
|
|
38
|
+
this.mesh.material.map.minFilter = LinearFilter
|
|
39
|
+
this.mesh.material.map.generateMipmaps = false
|
|
40
|
+
this.mesh.material.map.needsUpdate = true
|
|
41
|
+
|
|
42
|
+
this.add(this.mesh)
|
|
43
|
+
|
|
44
|
+
if (frame) {
|
|
45
|
+
this.frame = frame
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get image() {
|
|
50
|
+
return this._image
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get data() {
|
|
54
|
+
return this._data
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
set material(value) {
|
|
58
|
+
this.mesh.material = value
|
|
59
|
+
this.frame = this.frame
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get material() {
|
|
63
|
+
return this.mesh.material
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
set frame(value) {
|
|
67
|
+
if (!this.data) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this._frame = value
|
|
72
|
+
|
|
73
|
+
const offsetRepeat = this.mesh.material.offsetRepeat
|
|
74
|
+
const frameData = this.data.frames[this._frame]
|
|
75
|
+
|
|
76
|
+
offsetRepeat.z = (frameData.rotated ? frameData.frame.h : frameData.frame.w) / this.image.width
|
|
77
|
+
offsetRepeat.w = (frameData.rotated ? frameData.frame.w : frameData.frame.h) / this.image.height
|
|
78
|
+
|
|
79
|
+
offsetRepeat.x = frameData.frame.x / this.image.width
|
|
80
|
+
offsetRepeat.y = 1. - frameData.frame.y / this.image.height - offsetRepeat.w
|
|
81
|
+
|
|
82
|
+
const scale = 1 / parseFloat(this.data.meta.scale)
|
|
83
|
+
|
|
84
|
+
this.mesh.scale.x = (frameData.rotated ? frameData.frame.h : frameData.frame.w) * scale * this._scale
|
|
85
|
+
this.mesh.scale.y = (frameData.rotated ? frameData.frame.w : frameData.frame.h) * scale * this._scale
|
|
86
|
+
|
|
87
|
+
this.mesh.position.x = (-(frameData.sourceSize.w - frameData.frame.w) * .5 + frameData.spriteSourceSize.x + frameData.sourceSize.w * (.5 - frameData.pivot.x)) * scale * this._scale
|
|
88
|
+
this.mesh.position.y = ((frameData.sourceSize.h - frameData.frame.h) * .5 - frameData.spriteSourceSize.y - frameData.sourceSize.h * (.5 - frameData.pivot.y)) * scale * this._scale
|
|
89
|
+
|
|
90
|
+
this.mesh.rotation.z = frameData.rotated ? Math.PI * .5 : 0
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get frame() {
|
|
94
|
+
return this._frame
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
|
|
2
|
+
import THREESprite from './THREESprite.js'
|
|
3
|
+
|
|
4
|
+
import Signal from '../../core/util/Signal'
|
|
5
|
+
import Ticker from '../../core/util/Ticker'
|
|
6
|
+
|
|
7
|
+
const SPRITESHEETS = new Map()
|
|
8
|
+
|
|
9
|
+
export default class THREESpriteAnimation extends THREESprite {
|
|
10
|
+
constructor(image, data, animation, {
|
|
11
|
+
speed = 1,
|
|
12
|
+
fps = 25,
|
|
13
|
+
loop = true,
|
|
14
|
+
reverse = false,
|
|
15
|
+
scale = 1,
|
|
16
|
+
autoplay = true,
|
|
17
|
+
} = {}) {
|
|
18
|
+
let animations = SPRITESHEETS.get(data)
|
|
19
|
+
if (!animations) {
|
|
20
|
+
animations = new Map()
|
|
21
|
+
for (const key in data.frames) {
|
|
22
|
+
const match = /(.*?)([0-9]+)[$\.]/.exec(key)
|
|
23
|
+
const animationName = match[1]
|
|
24
|
+
let frames = animations.get(animationName)
|
|
25
|
+
if (!frames) {
|
|
26
|
+
frames = []
|
|
27
|
+
animations.set(animationName, frames)
|
|
28
|
+
}
|
|
29
|
+
const position = parseInt(match[2])
|
|
30
|
+
frames[position - 1] = key
|
|
31
|
+
}
|
|
32
|
+
SPRITESHEETS.set(data, animations)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
super(image, {
|
|
36
|
+
data,
|
|
37
|
+
frame: animations.get(animation)[0],
|
|
38
|
+
scale,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
this.onAnimationEnd = new Signal()
|
|
42
|
+
|
|
43
|
+
this._progress = 0
|
|
44
|
+
this._animations = animations
|
|
45
|
+
|
|
46
|
+
this.loop = loop
|
|
47
|
+
this.reverse = reverse
|
|
48
|
+
this.speed = speed
|
|
49
|
+
this.fps = fps
|
|
50
|
+
this.animation = animation
|
|
51
|
+
|
|
52
|
+
if (autoplay) {
|
|
53
|
+
this.play()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
set animation(value) {
|
|
58
|
+
if (this._animation === value) {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
this._animation = value
|
|
62
|
+
|
|
63
|
+
this.update()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get animation() {
|
|
67
|
+
return this._animation
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
play() {
|
|
71
|
+
Ticker.add(this._updateBound = this._updateBound || this.update.bind(this))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
stop() {
|
|
75
|
+
Ticker.delete(this._updateBound)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
set progress(value) {
|
|
79
|
+
if (this._progress === value) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
const previousProgress = this._progress
|
|
83
|
+
this._progress = value
|
|
84
|
+
if (this.loop) {
|
|
85
|
+
this._progress = ((this._progress % 1) + 1) % 1
|
|
86
|
+
} else {
|
|
87
|
+
this._progress = Math.min(Math.max(this._progress, 0), 1)
|
|
88
|
+
if (previousProgress !== this._progress && (this._progress === 1 && !this.reverse || this._progress === 0 && this.reverse)) {
|
|
89
|
+
this.onAnimationEnd.dispatch()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get progress() {
|
|
95
|
+
return this._progress
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
update() {
|
|
99
|
+
const frames = this._animations.get(this.animation)
|
|
100
|
+
this.progress += (this.speed * (this.fps / 60) * Ticker.timeScale / frames.length) * (this.reverse ? -1 : 1)
|
|
101
|
+
this.frame = frames[Math.round(this._progress * (frames.length - 1))]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { Object3D } from '../../../three/src/core/Object3D.js'
|
|
2
|
+
import { Mesh } from '../../../three/src/objects/Mesh.js'
|
|
3
|
+
import { PlaneGeometry } from '../../../three/src/geometries/PlaneGeometry.js'
|
|
4
|
+
import { Texture } from '../../../three/src/textures/Texture.js'
|
|
5
|
+
import { LinearFilter } from '../../../three/src/constants.js'
|
|
6
|
+
|
|
7
|
+
import THREEShaderMaterial from '../material/THREEShaderMaterial.js'
|
|
8
|
+
|
|
9
|
+
export default class THREEText extends Object3D {
|
|
10
|
+
constructor({
|
|
11
|
+
textContent = '',
|
|
12
|
+
font = '10px sans-serif',
|
|
13
|
+
fillStyle = 'black',
|
|
14
|
+
textAlign = 'start',
|
|
15
|
+
shadowColor = 'rgba(0, 0, 0 ,0)',
|
|
16
|
+
shadowBlur = 0,
|
|
17
|
+
shadowOffsetX = 0,
|
|
18
|
+
shadowOffsetY = 0,
|
|
19
|
+
scale = 1,
|
|
20
|
+
maxWidth = Infinity,
|
|
21
|
+
geometry = new PlaneGeometry(1, 1),
|
|
22
|
+
material = new THREEShaderMaterial({
|
|
23
|
+
type: 'basic',
|
|
24
|
+
transparent: true,
|
|
25
|
+
}),
|
|
26
|
+
} = {}) {
|
|
27
|
+
super()
|
|
28
|
+
|
|
29
|
+
this._scale = scale
|
|
30
|
+
|
|
31
|
+
this._canvas = document.createElement('canvas')
|
|
32
|
+
this._context = this._canvas.getContext('2d')
|
|
33
|
+
|
|
34
|
+
this._texture = new Texture(this._canvas)
|
|
35
|
+
|
|
36
|
+
material.map = this._texture
|
|
37
|
+
|
|
38
|
+
this.textContent = textContent
|
|
39
|
+
this.font = font
|
|
40
|
+
this.fillStyle = fillStyle
|
|
41
|
+
this.textAlign = textAlign
|
|
42
|
+
this.maxWidth = maxWidth
|
|
43
|
+
this.shadowColor = shadowColor
|
|
44
|
+
this.shadowBlur = shadowBlur
|
|
45
|
+
this.shadowOffsetX = shadowOffsetX
|
|
46
|
+
this.shadowOffsetY = shadowOffsetY
|
|
47
|
+
|
|
48
|
+
this._mesh = new Mesh(geometry, material)
|
|
49
|
+
this.add(this._mesh)
|
|
50
|
+
|
|
51
|
+
this._update()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_updateContextProperties() {
|
|
55
|
+
this._context.font = this.font
|
|
56
|
+
this._context.fillStyle = this.fillStyle
|
|
57
|
+
this._context.shadowColor = this.shadowColor
|
|
58
|
+
this._context.shadowBlur = this.shadowBlur
|
|
59
|
+
this._context.shadowOffsetX = this.shadowOffsetX
|
|
60
|
+
this._context.shadowOffsetY = this.shadowOffsetY
|
|
61
|
+
this._context.textBaseline = 'top'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_update() {
|
|
65
|
+
if (!this._mesh) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this._updateContextProperties()
|
|
70
|
+
|
|
71
|
+
const shadowOffsetX = this.shadowOffsetX - this.shadowBlur
|
|
72
|
+
const shadowOffsetY = this.shadowOffsetY - this.shadowBlur
|
|
73
|
+
|
|
74
|
+
const words = this.textContent.split(' ')
|
|
75
|
+
|
|
76
|
+
const spaceWidth = this._context.measureText(' ').width
|
|
77
|
+
const wordsWidth = new Map()
|
|
78
|
+
const lines = [{
|
|
79
|
+
textContent: '',
|
|
80
|
+
width: 0,
|
|
81
|
+
}]
|
|
82
|
+
for (const word of words) {
|
|
83
|
+
if (!wordsWidth.get(word)) {
|
|
84
|
+
wordsWidth.set(word, this._context.measureText(word).width)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let width = 0
|
|
89
|
+
let lineNumber = 0
|
|
90
|
+
for (const word of words) {
|
|
91
|
+
const newWidth = lines[lineNumber].width + wordsWidth.get(word)
|
|
92
|
+
|
|
93
|
+
if (newWidth > this.maxWidth) {
|
|
94
|
+
lineNumber++
|
|
95
|
+
lines[lineNumber] = {
|
|
96
|
+
textContent: word,
|
|
97
|
+
width: wordsWidth.get(word),
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
if (lines[lineNumber].textContent !== '') {
|
|
101
|
+
lines[lineNumber].textContent += ' '
|
|
102
|
+
}
|
|
103
|
+
lines[lineNumber].textContent += word
|
|
104
|
+
lines[lineNumber].width += spaceWidth + wordsWidth.get(word)
|
|
105
|
+
}
|
|
106
|
+
width = Math.max(width, lines[lineNumber].width)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
width += this.shadowBlur * 2 + Math.abs(this.shadowOffsetX)
|
|
110
|
+
|
|
111
|
+
const lineHeight = parseFloat(/\b(\d*)px/.exec(this._context.font)[1])
|
|
112
|
+
let height = lineHeight
|
|
113
|
+
height *= lines.length
|
|
114
|
+
height += this.shadowBlur * 2 + Math.abs(this.shadowOffsetY)
|
|
115
|
+
|
|
116
|
+
if (this._canvas.width !== width || this._canvas.height !== height) {
|
|
117
|
+
this._canvas.width = width
|
|
118
|
+
this._canvas.height = height
|
|
119
|
+
this._updateContextProperties()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this._mesh.position.y = -shadowOffsetY * .5 * this._scale
|
|
123
|
+
|
|
124
|
+
if (this.textAlign === 'start' || this.textAlign === 'left') {
|
|
125
|
+
this._mesh.position.x = (this._canvas.width * .5 + Math.min(0, shadowOffsetX)) * this._scale
|
|
126
|
+
} else if (this.textAlign === 'end' || this.textAlign === 'right') {
|
|
127
|
+
this._mesh.position.x = (-this._canvas.width * .5 + Math.max(0, shadowOffsetX)) * this._scale
|
|
128
|
+
} else {
|
|
129
|
+
this._mesh.position.x = shadowOffsetX * .5 * this._scale
|
|
130
|
+
}
|
|
131
|
+
this._mesh.scale.x = this._canvas.width * this._scale
|
|
132
|
+
this._mesh.scale.y = this._canvas.height * this._scale
|
|
133
|
+
this._context.globalAlpha = 1 / 255
|
|
134
|
+
this._context.fillRect(0, 0, width, height)
|
|
135
|
+
this._context.globalAlpha = 1
|
|
136
|
+
for (const [i, line] of lines.entries()) {
|
|
137
|
+
let offsetX
|
|
138
|
+
switch (this.textAlign) {
|
|
139
|
+
case 'start':
|
|
140
|
+
case 'left':
|
|
141
|
+
offsetX = 0
|
|
142
|
+
break
|
|
143
|
+
case 'center':
|
|
144
|
+
offsetX = (width - line.width) * .5
|
|
145
|
+
break
|
|
146
|
+
case 'end':
|
|
147
|
+
case 'right':
|
|
148
|
+
offsetX = width - line.width
|
|
149
|
+
break
|
|
150
|
+
}
|
|
151
|
+
this._context.fillText(line.textContent, offsetX + (shadowOffsetX < 0 ? Math.abs(shadowOffsetX) : 0), (shadowOffsetY < 0 ? Math.abs(shadowOffsetY) : 0) + lineHeight * i)
|
|
152
|
+
}
|
|
153
|
+
this._texture.needsUpdate = true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get material() {
|
|
157
|
+
return this._mesh.material
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
set textContent(value) {
|
|
161
|
+
this._textContent = value
|
|
162
|
+
this._update()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
get textContent() {
|
|
166
|
+
return this._textContent
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
set font(value) {
|
|
170
|
+
this._context.font = this._font = value
|
|
171
|
+
this._update()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get font() {
|
|
175
|
+
return this._font
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
set fillStyle(value) {
|
|
179
|
+
this._context.fillStyle = this._fillStyle = value
|
|
180
|
+
this._update()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
get fillStyle() {
|
|
184
|
+
return this._fillStyle
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
set textAlign(value) {
|
|
188
|
+
this._textAlign = value
|
|
189
|
+
this._update()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get textAlign() {
|
|
193
|
+
return this._textAlign
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
set maxWidth(value) {
|
|
197
|
+
this._maxWidth = value
|
|
198
|
+
this._update()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
get maxWidth() {
|
|
202
|
+
return this._maxWidth
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
set shadowColor(value) {
|
|
206
|
+
this._context.shadowColor = this._shadowColor = value
|
|
207
|
+
this._update()
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
get shadowColor() {
|
|
211
|
+
return this._shadowColor
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
set shadowBlur(value) {
|
|
215
|
+
this._context.shadowBlur = this._shadowBlur = value
|
|
216
|
+
this._update()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
get shadowBlur() {
|
|
220
|
+
return this._shadowBlur
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
set shadowOffsetX(value) {
|
|
224
|
+
this._context.shadowOffsetX = this._shadowOffsetX = value
|
|
225
|
+
this._update()
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
get shadowOffsetX() {
|
|
229
|
+
return this._shadowOffsetX
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
set shadowOffsetY(value) {
|
|
233
|
+
this._context.shadowOffsetY = this._shadowOffsetY = value
|
|
234
|
+
this._update()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
get shadowOffsetY() {
|
|
238
|
+
return this._shadowOffsetY
|
|
239
|
+
}
|
|
240
|
+
}
|