bg2e-js 2.3.11 → 2.3.13

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 (148) hide show
  1. package/dist/bg2e-js.js +356 -326
  2. package/dist/bg2e-js.js.map +1 -1
  3. package/package.json +56 -56
  4. package/src/app/AppController.ts +39 -39
  5. package/src/app/Bg2KeyboardEvent.ts +54 -54
  6. package/src/app/Bg2MouseEvent.ts +82 -82
  7. package/src/app/Bg2TouchEvent.ts +18 -18
  8. package/src/app/Canvas.ts +108 -108
  9. package/src/app/EventBase.ts +10 -10
  10. package/src/app/MainLoop.ts +273 -273
  11. package/src/app/index.ts +24 -24
  12. package/src/base/Color.ts +134 -134
  13. package/src/base/Environment.ts +183 -183
  14. package/src/base/Light.ts +192 -192
  15. package/src/base/Material.ts +620 -620
  16. package/src/base/PolyList.ts +365 -365
  17. package/src/base/Texture.ts +620 -620
  18. package/src/base/index.ts +81 -81
  19. package/src/db/Bg2LoaderPlugin.ts +143 -143
  20. package/src/db/DBPluginApi.ts +48 -48
  21. package/src/db/Loader.ts +116 -116
  22. package/src/db/LoaderPlugin.ts +34 -34
  23. package/src/db/MtlParser.ts +7 -7
  24. package/src/db/ObjLoaderPlugin.ts +54 -54
  25. package/src/db/ObjParser.ts +252 -252
  26. package/src/db/ObjWriterPlugin.ts +18 -18
  27. package/src/db/VitscnjLoaderPlugin.ts +112 -112
  28. package/src/db/Writer.ts +52 -52
  29. package/src/db/WriterPlugin.ts +22 -22
  30. package/src/db/index.ts +44 -44
  31. package/src/debug/DebugRenderer.ts +173 -173
  32. package/src/debug/WebGLTextureViewer.ts +75 -75
  33. package/src/debug/index.ts +6 -6
  34. package/src/index.html +11 -11
  35. package/src/index.ts +33 -33
  36. package/src/manipulation/SelectionBuffer.ts +81 -81
  37. package/src/manipulation/SelectionHighlight.ts +105 -84
  38. package/src/manipulation/SelectionIdAssignVisitor.ts +96 -96
  39. package/src/manipulation/SelectionManager.ts +196 -188
  40. package/src/manipulation/SelectionMode.ts +6 -6
  41. package/src/math/Mat3.ts +259 -259
  42. package/src/math/Mat4.ts +710 -710
  43. package/src/math/MatrixStrategy.ts +25 -25
  44. package/src/math/Quat.ts +65 -65
  45. package/src/math/Vec.ts +753 -753
  46. package/src/math/constants.ts +46 -46
  47. package/src/math/functions.ts +103 -103
  48. package/src/math/index.ts +74 -74
  49. package/src/phsics/joint.ts +137 -137
  50. package/src/primitives/arrow.ts +57 -57
  51. package/src/primitives/cone.ts +138 -138
  52. package/src/primitives/cube.ts +60 -60
  53. package/src/primitives/cylinder.ts +216 -216
  54. package/src/primitives/index.ts +13 -13
  55. package/src/primitives/plane.ts +31 -31
  56. package/src/primitives/sphere.ts +809 -809
  57. package/src/react/useBg2e.ts +69 -69
  58. package/src/render/BRDFIntegrationMap.ts +4 -4
  59. package/src/render/Environment.ts +135 -135
  60. package/src/render/FrameBuffer.ts +35 -35
  61. package/src/render/MaterialRenderer.ts +34 -34
  62. package/src/render/Pipeline.ts +108 -108
  63. package/src/render/PolyListRenderer.ts +47 -47
  64. package/src/render/RenderBuffer.ts +197 -197
  65. package/src/render/RenderQueue.ts +198 -198
  66. package/src/render/RenderState.ts +116 -116
  67. package/src/render/Renderer.ts +248 -248
  68. package/src/render/SceneAppController.ts +250 -250
  69. package/src/render/SceneRenderer.ts +387 -387
  70. package/src/render/Shader.ts +32 -32
  71. package/src/render/ShadowRenderer.ts +176 -176
  72. package/src/render/SkyCube.ts +105 -105
  73. package/src/render/SkySphere.ts +117 -117
  74. package/src/render/TextureMergerRenderer.ts +70 -70
  75. package/src/render/TextureRenderer.ts +34 -34
  76. package/src/render/index.ts +67 -67
  77. package/src/render/webgl/FrameBuffer.ts +9 -9
  78. package/src/render/webgl/MaterialRenderer.ts +112 -112
  79. package/src/render/webgl/Pipeline.ts +88 -88
  80. package/src/render/webgl/PolyListRenderer.ts +260 -260
  81. package/src/render/webgl/RenderBuffer.ts +226 -226
  82. package/src/render/webgl/Renderer.ts +262 -262
  83. package/src/render/webgl/SceneRenderer.ts +67 -67
  84. package/src/render/webgl/ShaderProgram.ts +424 -424
  85. package/src/render/webgl/ShadowRenderer.ts +6 -6
  86. package/src/render/webgl/SkyCube.ts +15 -15
  87. package/src/render/webgl/SkySphere.ts +15 -15
  88. package/src/render/webgl/State.ts +152 -152
  89. package/src/render/webgl/TextureRenderer.ts +167 -167
  90. package/src/render/webgl/VertexBuffer.ts +137 -137
  91. package/src/render/webgl/index.ts +35 -35
  92. package/src/scene/Camera.ts +458 -458
  93. package/src/scene/Chain.ts +44 -44
  94. package/src/scene/ChainJoint.ts +58 -58
  95. package/src/scene/Component.ts +177 -177
  96. package/src/scene/ComponentMap.ts +106 -106
  97. package/src/scene/Drawable.ts +154 -154
  98. package/src/scene/EnvironmentComponent.ts +141 -141
  99. package/src/scene/FindNodeVisitor.ts +59 -59
  100. package/src/scene/LightComponent.ts +154 -154
  101. package/src/scene/MatrixState.ts +46 -46
  102. package/src/scene/Node.ts +328 -328
  103. package/src/scene/NodeVisitor.ts +15 -15
  104. package/src/scene/OrbitCameraController.ts +450 -450
  105. package/src/scene/SmoothOrbitCameraController.ts +99 -99
  106. package/src/scene/Transform.ts +73 -73
  107. package/src/scene/index.ts +60 -60
  108. package/src/shaders/BasicDiffuseColorShader.ts +111 -111
  109. package/src/shaders/BasicPBRLightShader.ts +276 -276
  110. package/src/shaders/DebugRenderShader.ts +97 -97
  111. package/src/shaders/DepthRenderShader.ts +127 -127
  112. package/src/shaders/IrradianceMapCubeShader.ts +115 -115
  113. package/src/shaders/PBRLightIBLShader.ts +486 -486
  114. package/src/shaders/PickSelectionShader.ts +101 -101
  115. package/src/shaders/PresentDebugFramebufferShader.ts +118 -118
  116. package/src/shaders/PresentTextureShader.ts +99 -99
  117. package/src/shaders/SelectionHighlightShader.ts +143 -127
  118. package/src/shaders/ShaderFunction.ts +318 -318
  119. package/src/shaders/SkyCubeShader.ts +93 -93
  120. package/src/shaders/SkySphereShader.ts +102 -102
  121. package/src/shaders/SpecularMapCubeShader.ts +164 -164
  122. package/src/shaders/TextureMergerShader.ts +171 -171
  123. package/src/shaders/index.ts +36 -36
  124. package/src/shaders/webgl/color_correction.glsl +47 -47
  125. package/src/shaders/webgl/constants.glsl +6 -6
  126. package/src/shaders/webgl/index.ts +70 -70
  127. package/src/shaders/webgl/normal_map.glsl +9 -9
  128. package/src/shaders/webgl/pbr.glsl +173 -173
  129. package/src/shaders/webgl/uniforms.glsl +91 -91
  130. package/src/shaders/webgl_shader_lib.ts +213 -213
  131. package/src/tools/BinaryResourceProvider.ts +14 -14
  132. package/src/tools/ImageResourceProvider.ts +66 -66
  133. package/src/tools/MaterialModifier.ts +446 -446
  134. package/src/tools/Resource.ts +203 -203
  135. package/src/tools/ResourceProvider.ts +69 -69
  136. package/src/tools/TextResourceProvider.ts +24 -24
  137. package/src/tools/TextureCache.ts +51 -51
  138. package/src/tools/TextureResourceDatabase.ts +100 -100
  139. package/src/tools/UserAgent.ts +362 -362
  140. package/src/tools/VideoResourceProvider.ts +50 -50
  141. package/src/tools/WriteStrategy.ts +22 -22
  142. package/src/tools/base64.ts +11 -11
  143. package/src/tools/crypto.ts +19 -19
  144. package/src/tools/endiantess.ts +13 -13
  145. package/src/tools/image.ts +18 -18
  146. package/src/tools/index.ts +41 -41
  147. package/src/tools/processType.ts +39 -39
  148. package/src/vite-env.d.ts +12 -12
@@ -1,458 +1,458 @@
1
- import Mat4 from "../math/Mat4";
2
- import Vec from "../math/Vec";
3
- import Component from "./Component";
4
- import MatrixStrategy from "../math/MatrixStrategy";
5
- import { radiansToDegrees } from "../math/functions";
6
- import NodeVisitor from "./NodeVisitor";
7
- import Node from "./Node";
8
-
9
- type ProjectionStrategyType = "PerspectiveProjectionMethod" | "OpticalProjectionMethod" | "OrthographicProjectionStrategy";
10
-
11
- interface ProjectionStrategyBaseConfig {
12
- type?: ProjectionStrategyType;
13
- near?: number;
14
- far?: number;
15
- viewport?: number[] | Vec;
16
- }
17
-
18
- interface PerspectiveProjectionConfig extends ProjectionStrategyBaseConfig {
19
- type?: "PerspectiveProjectionMethod";
20
- fov?: number;
21
- }
22
-
23
- interface OpticalProjectionConfig extends ProjectionStrategyBaseConfig {
24
- type?: "OpticalProjectionMethod";
25
- frameSize?: number;
26
- focalLength?: number;
27
- }
28
-
29
- interface OrthographicProjectionConfig extends ProjectionStrategyBaseConfig {
30
- type?: "OrthographicProjectionStrategy";
31
- viewWidth?: number;
32
- }
33
-
34
- type ProjectionStrategyConfig =
35
- | PerspectiveProjectionConfig
36
- | OpticalProjectionConfig
37
- | OrthographicProjectionConfig
38
- | ProjectionStrategyBaseConfig;
39
-
40
- interface CameraSceneData {
41
- focusDistance?: number;
42
- isMain?: boolean;
43
- projectionMethod?: ProjectionStrategyConfig;
44
- [key: string]: unknown;
45
- }
46
-
47
- export abstract class ProjectionStrategy extends MatrixStrategy {
48
- protected _near: number;
49
- protected _far: number;
50
- protected _viewport: Vec;
51
-
52
- static Factory(jsonData?: ProjectionStrategyConfig | null): ProjectionStrategy | null {
53
- if (!jsonData) {
54
- return null;
55
- }
56
-
57
- const create = (): ProjectionStrategy | null => {
58
- switch (jsonData?.type) {
59
- case "PerspectiveProjectionMethod":
60
- return new PerspectiveProjectionStrategy();
61
- case "OpticalProjectionMethod":
62
- return new OpticalProjectionStrategy();
63
- case "OrthographicProjectionStrategy":
64
- return new OrthographicProjectionStrategy();
65
- default:
66
- return null;
67
- }
68
- };
69
-
70
- const result = create();
71
- if (result) {
72
- result.deserialize(jsonData);
73
- }
74
- return result;
75
- }
76
-
77
- constructor(target: Mat4 | null = null) {
78
- super(target);
79
-
80
- this._near = 0.1;
81
- this._far = 1000.0;
82
- this._viewport = new Vec([0, 0, 512, 512]);
83
- }
84
-
85
- abstract clone(): ProjectionStrategy;
86
-
87
- assign(other: ProjectionStrategy): void {
88
- this.near = other.near;
89
- this.far = other.far;
90
- this.viewport = new Vec(other.viewport);
91
- }
92
-
93
- set near(v: number) { this._near = v; this.apply(); }
94
- get near(): number { return this._near; }
95
- set far(v: number) { this._far = v; this.apply(); }
96
- get far(): number { return this._far; }
97
- set viewport(v: Vec) { this._viewport = v; this.apply(); }
98
- get viewport(): Vec { return this._viewport; }
99
-
100
- get fov(): number { return 0; }
101
-
102
- serialize(jsonData: ProjectionStrategyConfig): void {
103
- jsonData.near = this.near;
104
- jsonData.far = this.far;
105
- jsonData.viewport = this.viewport;
106
- }
107
-
108
- deserialize(jsonData: ProjectionStrategyConfig = {}): void {
109
- this.near = jsonData.near ?? this.near;
110
- this.far = jsonData.far ?? this.far;
111
- if (Array.isArray(jsonData.viewport)) {
112
- this.viewport = new Vec(jsonData.viewport);
113
- }
114
- else if (jsonData.viewport instanceof Vec) {
115
- this.viewport = new Vec(jsonData.viewport);
116
- }
117
- }
118
- }
119
-
120
- export class PerspectiveProjectionStrategy extends ProjectionStrategy {
121
- private _fov: number;
122
-
123
- constructor(target: Mat4 | null = null) {
124
- super(target);
125
- this._fov = 60;
126
- }
127
-
128
- set fov(f: number) { this._fov = f; this.apply(); }
129
- get fov(): number { return this._fov; }
130
-
131
- clone(): PerspectiveProjectionStrategy {
132
- const other = new PerspectiveProjectionStrategy(this.target);
133
- other.assign(this);
134
- return other;
135
- }
136
-
137
- assign(other: PerspectiveProjectionStrategy): void {
138
- super.assign(other);
139
- this.fov = other.fov;
140
- }
141
-
142
- apply(): void {
143
- if (this.target) {
144
- this.target.perspective(this.fov, this.viewport.aspectRatio, this.near, this.far);
145
- }
146
- }
147
-
148
- serialize(jsonData: PerspectiveProjectionConfig): void {
149
- jsonData.type = "PerspectiveProjectionMethod";
150
- jsonData.fov = this.fov;
151
- super.serialize(jsonData);
152
- }
153
-
154
- deserialize(jsonData: PerspectiveProjectionConfig = {}): void {
155
- super.deserialize(jsonData);
156
- this.fov = jsonData.fov ?? this.fov;
157
- }
158
- }
159
-
160
- export class OpticalProjectionStrategy extends ProjectionStrategy {
161
- private _focalLength: number;
162
- private _frameSize: number;
163
-
164
- constructor(target: Mat4 | null = null) {
165
- super(target);
166
- this._focalLength = 50;
167
- this._frameSize = 35;
168
- }
169
-
170
- set focalLength(v: number) { this._focalLength = v; this.apply(); }
171
- get focalLength(): number { return this._focalLength; }
172
- set frameSize(v: number) { this._frameSize = v; this.apply(); }
173
- get frameSize(): number { return this._frameSize; }
174
-
175
- get fov(): number {
176
- return 2 * Math.atan((this.frameSize / 2.0) / this.focalLength);
177
- }
178
-
179
- clone(): OpticalProjectionStrategy {
180
- const other = new OpticalProjectionStrategy(this.target);
181
- other.assign(this);
182
- return other;
183
- }
184
-
185
- assign(other: OpticalProjectionStrategy): void {
186
- super.assign(other);
187
- this.focalLength = other.focalLength;
188
- this.frameSize = other.frameSize;
189
- }
190
-
191
- apply(): void {
192
- if (this.target) {
193
- const fov = radiansToDegrees(this.fov);
194
- this.target.perspective(fov, this.viewport.aspectRatio, this.near, this.far);
195
- }
196
- }
197
-
198
- serialize(jsonData: OpticalProjectionConfig): void {
199
- super.serialize(jsonData);
200
- jsonData.type = "OpticalProjectionMethod";
201
- jsonData.frameSize = this.frameSize;
202
- jsonData.focalLength = this.focalLength;
203
- }
204
-
205
- deserialize(jsonData: OpticalProjectionConfig = {}): void {
206
- super.deserialize(jsonData);
207
- this.frameSize = jsonData.frameSize ?? this.frameSize;
208
- this.focalLength = jsonData.focalLength ?? this.focalLength;
209
- }
210
- }
211
-
212
- export class OrthographicProjectionStrategy extends ProjectionStrategy {
213
- private _viewWidth: number;
214
-
215
- constructor(target: Mat4 | null = null) {
216
- super(target);
217
- this._viewWidth = 100;
218
- }
219
-
220
- set viewWidth(v: number) { this._viewWidth = v; this.apply(); }
221
- get viewWidth(): number { return this._viewWidth; }
222
-
223
- clone(): OrthographicProjectionStrategy {
224
- const other = new OrthographicProjectionStrategy(this.target);
225
- other.assign(this);
226
- return other;
227
- }
228
-
229
- asign(other: OrthographicProjectionStrategy): void {
230
- super.assign(other);
231
- this.viewWidth = other.viewWidth;
232
- }
233
-
234
- apply(): void {
235
- if (this.target) {
236
- const height = this.viewWidth / this.viewport.aspectRatio;
237
- const x = this.viewWidth / 2;
238
- const y = height / 2;
239
- this.target.ortho(-x, x, -y, y, -this._far, this._far);
240
- }
241
- }
242
-
243
- serialize(jsonData: OrthographicProjectionConfig): void {
244
- jsonData.type = "OrthographicProjectionStrategy";
245
- jsonData.viewWidth = this.viewWidth;
246
- super.serialize(jsonData);
247
- }
248
-
249
- deserialize(jsonData: OrthographicProjectionConfig = {}): void {
250
- this.viewWidth = jsonData.viewWidth ?? this.viewWidth;
251
- super.deserialize(jsonData);
252
- }
253
- }
254
-
255
- class SetMainCameraVisitor extends NodeVisitor {
256
- private readonly _mainCamera: Camera;
257
-
258
- constructor(mainCamera: Camera) {
259
- super();
260
- if (!mainCamera) {
261
- throw Error("Set main camera: invalid parameter. The camera paremeter is null.")
262
- }
263
- if (!(mainCamera instanceof Camera)) {
264
- throw Error("Set main camera: invalid parameter. The object is not an instance of Camera class.")
265
- }
266
- this._mainCamera = mainCamera;
267
- }
268
-
269
- visit(node: Node): void {
270
- const cam = node.camera;
271
- if (!(cam instanceof Camera)) {
272
- return;
273
- }
274
- cam._isMain = cam === this._mainCamera;
275
- }
276
- }
277
-
278
- class GetMainCameraVisitor extends NodeVisitor {
279
- private _mainCamera: Camera | null;
280
- private _firstCameraFound: Camera | null;
281
-
282
- constructor() {
283
- super();
284
- this._mainCamera = null;
285
- this._firstCameraFound = null;
286
- }
287
-
288
- get mainCamera(): Camera | null {
289
- return this._mainCamera;
290
- }
291
-
292
- get firstCameraFound(): Camera | null {
293
- return this._firstCameraFound;
294
- }
295
-
296
- clear(): void {
297
- this._mainCamera = null;
298
- this._firstCameraFound = null;
299
- }
300
-
301
- visit(node: Node): void {
302
- // Note: The _isMain flag is set in Camera.SetMain() function
303
- const cam = node.camera;
304
- if (!(cam instanceof Camera)) {
305
- return;
306
- }
307
-
308
- if (cam.isMain) {
309
- cam._isMain = false;
310
- if (this._mainCamera) {
311
- console.warn("More than one main cameras found in the scene");
312
- }
313
- this._mainCamera = cam;
314
- }
315
- else if (!this._firstCameraFound) {
316
- cam._isMain = false;
317
- this._firstCameraFound = cam;
318
- }
319
- }
320
- }
321
-
322
- export default class Camera extends Component {
323
- private _projectionStrategy: ProjectionStrategy | null;
324
- public _isMain: boolean;
325
- private _projectionMatrix: Mat4;
326
- private _viewport: Vec;
327
- private _focusDistance: number;
328
-
329
- static SetMain(sceneRoot: Node, camera: Camera): void {
330
- const isNode = sceneRoot instanceof Node;
331
- if (!isNode || sceneRoot.parent !== null) {
332
- throw Error("Camera.setMain(): invalid parameter. Object is not a scene root");
333
- }
334
- const visitor = new SetMainCameraVisitor(camera);
335
- sceneRoot.accept(visitor);
336
- const rootWithMain = sceneRoot as Node & { __mainCamera__?: Camera | null };
337
- rootWithMain.__mainCamera__ = camera;
338
- }
339
-
340
- static GetMain(sceneRoot: Node): Camera | null {
341
- const rootWithMain = sceneRoot as Node & { __mainCamera__?: Camera | null };
342
- if (!rootWithMain.__mainCamera__) {
343
- const visitor = new GetMainCameraVisitor();
344
- sceneRoot.accept(visitor);
345
- rootWithMain.__mainCamera__ = visitor.mainCamera || visitor.firstCameraFound || null;
346
- if (rootWithMain.__mainCamera__) {
347
- rootWithMain.__mainCamera__._isMain = true;
348
- }
349
- }
350
- return rootWithMain.__mainCamera__ ?? null;
351
- }
352
-
353
- setMain(sceneRoot: Node): void {
354
- Camera.SetMain(sceneRoot, this);
355
- }
356
-
357
- constructor() {
358
- super("Camera");
359
-
360
- this._projectionStrategy = null;
361
- this._isMain = false;
362
-
363
- this._projectionMatrix = Mat4.MakePerspective(45.0, 1, 0.1, 100.0);
364
- this._viewport = new Vec(0, 0, 512, 512);
365
-
366
- this._focusDistance = 5;
367
- }
368
-
369
- clone(): Camera {
370
- const other = new Camera();
371
- other.assign(this);
372
- return other;
373
- }
374
-
375
- assign(other: Camera): void {
376
- other._projectionStrategy = this._projectionStrategy?.clone() || null;
377
- // This attribute cannot be assigned, because there can only be one main camera.
378
- other._isMain = false;
379
- other._projectionMatrix = new Mat4(this._projectionMatrix);
380
- other._viewport = new Vec(this._viewport);
381
- other._focusDistance = this._focusDistance;
382
- }
383
-
384
- get isMain(): boolean {
385
- return this._isMain;
386
- }
387
-
388
- get projectionMatrix(): Mat4 {
389
- return this._projectionMatrix;
390
- }
391
-
392
- set projectionMatrix(p: Mat4) {
393
- this._projectionStrategy = null;
394
- this._projectionMatrix = p;
395
- }
396
-
397
- get viewport(): Vec {
398
- return this._viewport;
399
- }
400
-
401
- set viewport(vp: Vec) {
402
- this._viewport = vp;
403
- }
404
-
405
- get projectionStrategy(): ProjectionStrategy | null {
406
- return this._projectionStrategy;
407
- }
408
-
409
- set projectionStrategy(ps: ProjectionStrategy | null) {
410
- this._projectionStrategy = ps;
411
- if (this._projectionStrategy) {
412
- this._projectionStrategy.target = this._projectionMatrix;
413
- this._projectionStrategy.viewport = this._viewport;
414
- this._projectionStrategy.apply();
415
- }
416
- }
417
-
418
- get focusDistance(): number {
419
- return this._focusDistance;
420
- }
421
-
422
- set focusDistance(fd: number) {
423
- this._focusDistance = fd;
424
- }
425
-
426
- // This function regenerate the projection matrix with the new
427
- // aspect ratio, if the projectionStrategy is set.
428
- resize(width: number, height: number): void {
429
- this._viewport = new Vec([0, 0, width, height]);
430
- if (this._projectionStrategy) {
431
- this._projectionStrategy.viewport = this._viewport;
432
- this._projectionStrategy.apply();
433
- }
434
- }
435
-
436
- async deserialize(sceneData: CameraSceneData = {}, loader: unknown): Promise<void> {
437
- await super.deserialize(sceneData, loader);
438
- this.focusDistance = sceneData.focusDistance ?? this._focusDistance;
439
- if (sceneData.projectionMethod) {
440
- const strategy = ProjectionStrategy.Factory(sceneData.projectionMethod);
441
- if (strategy) {
442
- this.projectionStrategy = strategy;
443
- }
444
- }
445
- }
446
-
447
- async serialize(sceneData: CameraSceneData = {}, writer: unknown): Promise<void> {
448
- await super.serialize(sceneData, writer);
449
- sceneData.isMain = this._isMain;
450
- sceneData.focusDistance = this._focusDistance;
451
- if (this.projectionStrategy) {
452
- const projMethod: ProjectionStrategyConfig = {};
453
- this.projectionStrategy.serialize(projMethod);
454
- sceneData.projectionMethod = projMethod;
455
- }
456
- }
457
- }
458
-
1
+ import Mat4 from "../math/Mat4";
2
+ import Vec from "../math/Vec";
3
+ import Component from "./Component";
4
+ import MatrixStrategy from "../math/MatrixStrategy";
5
+ import { radiansToDegrees } from "../math/functions";
6
+ import NodeVisitor from "./NodeVisitor";
7
+ import Node from "./Node";
8
+
9
+ type ProjectionStrategyType = "PerspectiveProjectionMethod" | "OpticalProjectionMethod" | "OrthographicProjectionStrategy";
10
+
11
+ interface ProjectionStrategyBaseConfig {
12
+ type?: ProjectionStrategyType;
13
+ near?: number;
14
+ far?: number;
15
+ viewport?: number[] | Vec;
16
+ }
17
+
18
+ interface PerspectiveProjectionConfig extends ProjectionStrategyBaseConfig {
19
+ type?: "PerspectiveProjectionMethod";
20
+ fov?: number;
21
+ }
22
+
23
+ interface OpticalProjectionConfig extends ProjectionStrategyBaseConfig {
24
+ type?: "OpticalProjectionMethod";
25
+ frameSize?: number;
26
+ focalLength?: number;
27
+ }
28
+
29
+ interface OrthographicProjectionConfig extends ProjectionStrategyBaseConfig {
30
+ type?: "OrthographicProjectionStrategy";
31
+ viewWidth?: number;
32
+ }
33
+
34
+ type ProjectionStrategyConfig =
35
+ | PerspectiveProjectionConfig
36
+ | OpticalProjectionConfig
37
+ | OrthographicProjectionConfig
38
+ | ProjectionStrategyBaseConfig;
39
+
40
+ interface CameraSceneData {
41
+ focusDistance?: number;
42
+ isMain?: boolean;
43
+ projectionMethod?: ProjectionStrategyConfig;
44
+ [key: string]: unknown;
45
+ }
46
+
47
+ export abstract class ProjectionStrategy extends MatrixStrategy {
48
+ protected _near: number;
49
+ protected _far: number;
50
+ protected _viewport: Vec;
51
+
52
+ static Factory(jsonData?: ProjectionStrategyConfig | null): ProjectionStrategy | null {
53
+ if (!jsonData) {
54
+ return null;
55
+ }
56
+
57
+ const create = (): ProjectionStrategy | null => {
58
+ switch (jsonData?.type) {
59
+ case "PerspectiveProjectionMethod":
60
+ return new PerspectiveProjectionStrategy();
61
+ case "OpticalProjectionMethod":
62
+ return new OpticalProjectionStrategy();
63
+ case "OrthographicProjectionStrategy":
64
+ return new OrthographicProjectionStrategy();
65
+ default:
66
+ return null;
67
+ }
68
+ };
69
+
70
+ const result = create();
71
+ if (result) {
72
+ result.deserialize(jsonData);
73
+ }
74
+ return result;
75
+ }
76
+
77
+ constructor(target: Mat4 | null = null) {
78
+ super(target);
79
+
80
+ this._near = 0.1;
81
+ this._far = 1000.0;
82
+ this._viewport = new Vec([0, 0, 512, 512]);
83
+ }
84
+
85
+ abstract clone(): ProjectionStrategy;
86
+
87
+ assign(other: ProjectionStrategy): void {
88
+ this.near = other.near;
89
+ this.far = other.far;
90
+ this.viewport = new Vec(other.viewport);
91
+ }
92
+
93
+ set near(v: number) { this._near = v; this.apply(); }
94
+ get near(): number { return this._near; }
95
+ set far(v: number) { this._far = v; this.apply(); }
96
+ get far(): number { return this._far; }
97
+ set viewport(v: Vec) { this._viewport = v; this.apply(); }
98
+ get viewport(): Vec { return this._viewport; }
99
+
100
+ get fov(): number { return 0; }
101
+
102
+ serialize(jsonData: ProjectionStrategyConfig): void {
103
+ jsonData.near = this.near;
104
+ jsonData.far = this.far;
105
+ jsonData.viewport = this.viewport;
106
+ }
107
+
108
+ deserialize(jsonData: ProjectionStrategyConfig = {}): void {
109
+ this.near = jsonData.near ?? this.near;
110
+ this.far = jsonData.far ?? this.far;
111
+ if (Array.isArray(jsonData.viewport)) {
112
+ this.viewport = new Vec(jsonData.viewport);
113
+ }
114
+ else if (jsonData.viewport instanceof Vec) {
115
+ this.viewport = new Vec(jsonData.viewport);
116
+ }
117
+ }
118
+ }
119
+
120
+ export class PerspectiveProjectionStrategy extends ProjectionStrategy {
121
+ private _fov: number;
122
+
123
+ constructor(target: Mat4 | null = null) {
124
+ super(target);
125
+ this._fov = 60;
126
+ }
127
+
128
+ set fov(f: number) { this._fov = f; this.apply(); }
129
+ get fov(): number { return this._fov; }
130
+
131
+ clone(): PerspectiveProjectionStrategy {
132
+ const other = new PerspectiveProjectionStrategy(this.target);
133
+ other.assign(this);
134
+ return other;
135
+ }
136
+
137
+ assign(other: PerspectiveProjectionStrategy): void {
138
+ super.assign(other);
139
+ this.fov = other.fov;
140
+ }
141
+
142
+ apply(): void {
143
+ if (this.target) {
144
+ this.target.perspective(this.fov, this.viewport.aspectRatio, this.near, this.far);
145
+ }
146
+ }
147
+
148
+ serialize(jsonData: PerspectiveProjectionConfig): void {
149
+ jsonData.type = "PerspectiveProjectionMethod";
150
+ jsonData.fov = this.fov;
151
+ super.serialize(jsonData);
152
+ }
153
+
154
+ deserialize(jsonData: PerspectiveProjectionConfig = {}): void {
155
+ super.deserialize(jsonData);
156
+ this.fov = jsonData.fov ?? this.fov;
157
+ }
158
+ }
159
+
160
+ export class OpticalProjectionStrategy extends ProjectionStrategy {
161
+ private _focalLength: number;
162
+ private _frameSize: number;
163
+
164
+ constructor(target: Mat4 | null = null) {
165
+ super(target);
166
+ this._focalLength = 50;
167
+ this._frameSize = 35;
168
+ }
169
+
170
+ set focalLength(v: number) { this._focalLength = v; this.apply(); }
171
+ get focalLength(): number { return this._focalLength; }
172
+ set frameSize(v: number) { this._frameSize = v; this.apply(); }
173
+ get frameSize(): number { return this._frameSize; }
174
+
175
+ get fov(): number {
176
+ return 2 * Math.atan((this.frameSize / 2.0) / this.focalLength);
177
+ }
178
+
179
+ clone(): OpticalProjectionStrategy {
180
+ const other = new OpticalProjectionStrategy(this.target);
181
+ other.assign(this);
182
+ return other;
183
+ }
184
+
185
+ assign(other: OpticalProjectionStrategy): void {
186
+ super.assign(other);
187
+ this.focalLength = other.focalLength;
188
+ this.frameSize = other.frameSize;
189
+ }
190
+
191
+ apply(): void {
192
+ if (this.target) {
193
+ const fov = radiansToDegrees(this.fov);
194
+ this.target.perspective(fov, this.viewport.aspectRatio, this.near, this.far);
195
+ }
196
+ }
197
+
198
+ serialize(jsonData: OpticalProjectionConfig): void {
199
+ super.serialize(jsonData);
200
+ jsonData.type = "OpticalProjectionMethod";
201
+ jsonData.frameSize = this.frameSize;
202
+ jsonData.focalLength = this.focalLength;
203
+ }
204
+
205
+ deserialize(jsonData: OpticalProjectionConfig = {}): void {
206
+ super.deserialize(jsonData);
207
+ this.frameSize = jsonData.frameSize ?? this.frameSize;
208
+ this.focalLength = jsonData.focalLength ?? this.focalLength;
209
+ }
210
+ }
211
+
212
+ export class OrthographicProjectionStrategy extends ProjectionStrategy {
213
+ private _viewWidth: number;
214
+
215
+ constructor(target: Mat4 | null = null) {
216
+ super(target);
217
+ this._viewWidth = 100;
218
+ }
219
+
220
+ set viewWidth(v: number) { this._viewWidth = v; this.apply(); }
221
+ get viewWidth(): number { return this._viewWidth; }
222
+
223
+ clone(): OrthographicProjectionStrategy {
224
+ const other = new OrthographicProjectionStrategy(this.target);
225
+ other.assign(this);
226
+ return other;
227
+ }
228
+
229
+ asign(other: OrthographicProjectionStrategy): void {
230
+ super.assign(other);
231
+ this.viewWidth = other.viewWidth;
232
+ }
233
+
234
+ apply(): void {
235
+ if (this.target) {
236
+ const height = this.viewWidth / this.viewport.aspectRatio;
237
+ const x = this.viewWidth / 2;
238
+ const y = height / 2;
239
+ this.target.ortho(-x, x, -y, y, -this._far, this._far);
240
+ }
241
+ }
242
+
243
+ serialize(jsonData: OrthographicProjectionConfig): void {
244
+ jsonData.type = "OrthographicProjectionStrategy";
245
+ jsonData.viewWidth = this.viewWidth;
246
+ super.serialize(jsonData);
247
+ }
248
+
249
+ deserialize(jsonData: OrthographicProjectionConfig = {}): void {
250
+ this.viewWidth = jsonData.viewWidth ?? this.viewWidth;
251
+ super.deserialize(jsonData);
252
+ }
253
+ }
254
+
255
+ class SetMainCameraVisitor extends NodeVisitor {
256
+ private readonly _mainCamera: Camera;
257
+
258
+ constructor(mainCamera: Camera) {
259
+ super();
260
+ if (!mainCamera) {
261
+ throw Error("Set main camera: invalid parameter. The camera paremeter is null.")
262
+ }
263
+ if (!(mainCamera instanceof Camera)) {
264
+ throw Error("Set main camera: invalid parameter. The object is not an instance of Camera class.")
265
+ }
266
+ this._mainCamera = mainCamera;
267
+ }
268
+
269
+ visit(node: Node): void {
270
+ const cam = node.camera;
271
+ if (!(cam instanceof Camera)) {
272
+ return;
273
+ }
274
+ cam._isMain = cam === this._mainCamera;
275
+ }
276
+ }
277
+
278
+ class GetMainCameraVisitor extends NodeVisitor {
279
+ private _mainCamera: Camera | null;
280
+ private _firstCameraFound: Camera | null;
281
+
282
+ constructor() {
283
+ super();
284
+ this._mainCamera = null;
285
+ this._firstCameraFound = null;
286
+ }
287
+
288
+ get mainCamera(): Camera | null {
289
+ return this._mainCamera;
290
+ }
291
+
292
+ get firstCameraFound(): Camera | null {
293
+ return this._firstCameraFound;
294
+ }
295
+
296
+ clear(): void {
297
+ this._mainCamera = null;
298
+ this._firstCameraFound = null;
299
+ }
300
+
301
+ visit(node: Node): void {
302
+ // Note: The _isMain flag is set in Camera.SetMain() function
303
+ const cam = node.camera;
304
+ if (!(cam instanceof Camera)) {
305
+ return;
306
+ }
307
+
308
+ if (cam.isMain) {
309
+ cam._isMain = false;
310
+ if (this._mainCamera) {
311
+ console.warn("More than one main cameras found in the scene");
312
+ }
313
+ this._mainCamera = cam;
314
+ }
315
+ else if (!this._firstCameraFound) {
316
+ cam._isMain = false;
317
+ this._firstCameraFound = cam;
318
+ }
319
+ }
320
+ }
321
+
322
+ export default class Camera extends Component {
323
+ private _projectionStrategy: ProjectionStrategy | null;
324
+ public _isMain: boolean;
325
+ private _projectionMatrix: Mat4;
326
+ private _viewport: Vec;
327
+ private _focusDistance: number;
328
+
329
+ static SetMain(sceneRoot: Node, camera: Camera): void {
330
+ const isNode = sceneRoot instanceof Node;
331
+ if (!isNode || sceneRoot.parent !== null) {
332
+ throw Error("Camera.setMain(): invalid parameter. Object is not a scene root");
333
+ }
334
+ const visitor = new SetMainCameraVisitor(camera);
335
+ sceneRoot.accept(visitor);
336
+ const rootWithMain = sceneRoot as Node & { __mainCamera__?: Camera | null };
337
+ rootWithMain.__mainCamera__ = camera;
338
+ }
339
+
340
+ static GetMain(sceneRoot: Node): Camera | null {
341
+ const rootWithMain = sceneRoot as Node & { __mainCamera__?: Camera | null };
342
+ if (!rootWithMain.__mainCamera__) {
343
+ const visitor = new GetMainCameraVisitor();
344
+ sceneRoot.accept(visitor);
345
+ rootWithMain.__mainCamera__ = visitor.mainCamera || visitor.firstCameraFound || null;
346
+ if (rootWithMain.__mainCamera__) {
347
+ rootWithMain.__mainCamera__._isMain = true;
348
+ }
349
+ }
350
+ return rootWithMain.__mainCamera__ ?? null;
351
+ }
352
+
353
+ setMain(sceneRoot: Node): void {
354
+ Camera.SetMain(sceneRoot, this);
355
+ }
356
+
357
+ constructor() {
358
+ super("Camera");
359
+
360
+ this._projectionStrategy = null;
361
+ this._isMain = false;
362
+
363
+ this._projectionMatrix = Mat4.MakePerspective(45.0, 1, 0.1, 100.0);
364
+ this._viewport = new Vec(0, 0, 512, 512);
365
+
366
+ this._focusDistance = 5;
367
+ }
368
+
369
+ clone(): Camera {
370
+ const other = new Camera();
371
+ other.assign(this);
372
+ return other;
373
+ }
374
+
375
+ assign(other: Camera): void {
376
+ other._projectionStrategy = this._projectionStrategy?.clone() || null;
377
+ // This attribute cannot be assigned, because there can only be one main camera.
378
+ other._isMain = false;
379
+ other._projectionMatrix = new Mat4(this._projectionMatrix);
380
+ other._viewport = new Vec(this._viewport);
381
+ other._focusDistance = this._focusDistance;
382
+ }
383
+
384
+ get isMain(): boolean {
385
+ return this._isMain;
386
+ }
387
+
388
+ get projectionMatrix(): Mat4 {
389
+ return this._projectionMatrix;
390
+ }
391
+
392
+ set projectionMatrix(p: Mat4) {
393
+ this._projectionStrategy = null;
394
+ this._projectionMatrix = p;
395
+ }
396
+
397
+ get viewport(): Vec {
398
+ return this._viewport;
399
+ }
400
+
401
+ set viewport(vp: Vec) {
402
+ this._viewport = vp;
403
+ }
404
+
405
+ get projectionStrategy(): ProjectionStrategy | null {
406
+ return this._projectionStrategy;
407
+ }
408
+
409
+ set projectionStrategy(ps: ProjectionStrategy | null) {
410
+ this._projectionStrategy = ps;
411
+ if (this._projectionStrategy) {
412
+ this._projectionStrategy.target = this._projectionMatrix;
413
+ this._projectionStrategy.viewport = this._viewport;
414
+ this._projectionStrategy.apply();
415
+ }
416
+ }
417
+
418
+ get focusDistance(): number {
419
+ return this._focusDistance;
420
+ }
421
+
422
+ set focusDistance(fd: number) {
423
+ this._focusDistance = fd;
424
+ }
425
+
426
+ // This function regenerate the projection matrix with the new
427
+ // aspect ratio, if the projectionStrategy is set.
428
+ resize(width: number, height: number): void {
429
+ this._viewport = new Vec([0, 0, width, height]);
430
+ if (this._projectionStrategy) {
431
+ this._projectionStrategy.viewport = this._viewport;
432
+ this._projectionStrategy.apply();
433
+ }
434
+ }
435
+
436
+ async deserialize(sceneData: CameraSceneData = {}, loader: unknown): Promise<void> {
437
+ await super.deserialize(sceneData, loader);
438
+ this.focusDistance = sceneData.focusDistance ?? this._focusDistance;
439
+ if (sceneData.projectionMethod) {
440
+ const strategy = ProjectionStrategy.Factory(sceneData.projectionMethod);
441
+ if (strategy) {
442
+ this.projectionStrategy = strategy;
443
+ }
444
+ }
445
+ }
446
+
447
+ async serialize(sceneData: CameraSceneData = {}, writer: unknown): Promise<void> {
448
+ await super.serialize(sceneData, writer);
449
+ sceneData.isMain = this._isMain;
450
+ sceneData.focusDistance = this._focusDistance;
451
+ if (this.projectionStrategy) {
452
+ const projMethod: ProjectionStrategyConfig = {};
453
+ this.projectionStrategy.serialize(projMethod);
454
+ sceneData.projectionMethod = projMethod;
455
+ }
456
+ }
457
+ }
458
+