@needle-tools/engine 2.56.2-pre → 2.58.0-pre

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 (73) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/needle-engine.d.ts +99 -160
  3. package/dist/needle-engine.js +279 -3420
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +18 -18
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/dist/needle-engine.tsbuildinfo +1 -1
  8. package/lib/engine/api.d.ts +2 -1
  9. package/lib/engine/api.js +2 -1
  10. package/lib/engine/api.js.map +1 -1
  11. package/lib/engine/codegen/register_types.js +2 -6
  12. package/lib/engine/codegen/register_types.js.map +1 -1
  13. package/lib/engine/engine.d.ts +2 -28
  14. package/lib/engine/engine.js +1 -0
  15. package/lib/engine/engine.js.map +1 -1
  16. package/lib/engine/engine_hot_reload.d.ts +3 -0
  17. package/lib/engine/engine_hot_reload.js +168 -0
  18. package/lib/engine/engine_hot_reload.js.map +1 -0
  19. package/lib/engine/engine_input.js +1 -1
  20. package/lib/engine/engine_scenetools.d.ts +1 -0
  21. package/lib/engine/engine_scenetools.js +8 -6
  22. package/lib/engine/engine_scenetools.js.map +1 -1
  23. package/lib/engine/engine_setup.d.ts +4 -2
  24. package/lib/engine/engine_setup.js +66 -50
  25. package/lib/engine/engine_setup.js.map +1 -1
  26. package/lib/engine/engine_three_utils.js.map +1 -1
  27. package/lib/engine/engine_utils_screenshot.d.ts +5 -0
  28. package/lib/engine/engine_utils_screenshot.js +32 -0
  29. package/lib/engine/engine_utils_screenshot.js.map +1 -0
  30. package/lib/engine-components/Fog.d.ts +20 -0
  31. package/lib/engine-components/Fog.js +61 -0
  32. package/lib/engine-components/Fog.js.map +1 -0
  33. package/lib/engine-components/Skybox.js +3 -1
  34. package/lib/engine-components/Skybox.js.map +1 -1
  35. package/lib/engine-components/WebXR.js +2 -2
  36. package/lib/engine-components/WebXR.js.map +1 -1
  37. package/lib/engine-components/codegen/components.d.ts +1 -0
  38. package/lib/engine-components/codegen/components.js +1 -0
  39. package/lib/engine-components/codegen/components.js.map +1 -1
  40. package/lib/engine-components/ui/EventSystem.d.ts +1 -1
  41. package/lib/engine-components/ui/EventSystem.js +25 -10
  42. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  43. package/lib/engine-components/ui/PointerEvents.d.ts +3 -1
  44. package/lib/engine-components/ui/PointerEvents.js +1 -0
  45. package/lib/engine-components/ui/PointerEvents.js.map +1 -1
  46. package/lib/engine-components/ui/Raycaster.js.map +1 -1
  47. package/lib/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +1 -2
  49. package/src/engine/api.ts +3 -2
  50. package/src/engine/codegen/register_types.js +4 -8
  51. package/src/engine/engine.ts +3 -1
  52. package/src/engine/engine_hot_reload.ts +186 -0
  53. package/src/engine/engine_input.ts +1 -1
  54. package/src/engine/engine_scenetools.ts +9 -6
  55. package/src/engine/engine_setup.ts +66 -52
  56. package/src/engine/engine_three_utils.ts +4 -4
  57. package/src/engine/engine_utils_screenshot.ts +41 -0
  58. package/src/engine-components/Fog.ts +60 -0
  59. package/src/engine-components/Skybox.ts +3 -1
  60. package/src/engine-components/TestRunner.ts +1 -1
  61. package/src/engine-components/WebXR.ts +2 -2
  62. package/src/engine-components/codegen/components.ts +1 -0
  63. package/src/engine-components/ui/EventSystem.ts +30 -15
  64. package/src/engine-components/ui/PointerEvents.ts +7 -4
  65. package/src/engine-components/ui/Raycaster.ts +6 -3
  66. package/lib/engine-components-experimental/annotation/LineDrawer.d.ts +0 -18
  67. package/lib/engine-components-experimental/annotation/LineDrawer.js +0 -175
  68. package/lib/engine-components-experimental/annotation/LineDrawer.js.map +0 -1
  69. package/lib/engine-components-experimental/annotation/LinesManager.d.ts +0 -54
  70. package/lib/engine-components-experimental/annotation/LinesManager.js +0 -155
  71. package/lib/engine-components-experimental/annotation/LinesManager.js.map +0 -1
  72. package/src/engine-components-experimental/annotation/LineDrawer.ts +0 -194
  73. package/src/engine-components-experimental/annotation/LinesManager.ts +0 -218
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "2.56.2-pre",
3
+ "version": "2.58.0-pre",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
5
5
  "main": "dist/needle-engine.js",
6
6
  "module": "src/needle-engine.ts",
@@ -49,7 +49,6 @@
49
49
  "stats.js": "^0.17.0",
50
50
  "three": "npm:@needle-tools/three@^0.146.2",
51
51
  "three-mesh-ui": "^6.4.5",
52
- "three.meshline": "^1.4.0",
53
52
  "three.quarks": "^0.7.3",
54
53
  "uuid": "^9.0.0",
55
54
  "websocket-ts": "^1.1.1"
package/src/engine/api.ts CHANGED
@@ -3,7 +3,8 @@ export { InstancingUtil } from "./engine_instancing";
3
3
  export * from "./engine_gameobject";
4
4
  export * from "./engine_components"
5
5
  export { AssetReference } from "./engine_addressables";
6
- export { FrameEvent } from "./engine_setup";
6
+ export { Context, FrameEvent } from "./engine_setup";
7
7
  export * from "./debug/debug";
8
8
  export { validate } from "./engine_util_decorator"
9
- export { Gizmos } from "./engine_gizmos"
9
+ export { Gizmos } from "./engine_gizmos"
10
+ export * from "./engine_scenetools";
@@ -1,5 +1,5 @@
1
1
  import { TypeStore } from "./../engine_typestore"
2
-
2
+
3
3
  // Import types
4
4
  import { __Ignore } from "../../engine-components/codegen/components.ts";
5
5
  import { AlignmentConstraint } from "../../engine-components/AlignmentConstraint.ts";
@@ -55,6 +55,7 @@ import { EventTrigger } from "../../engine-components/EventTrigger.ts";
55
55
  import { FieldWithDefault } from "../../engine-components/Renderer.ts";
56
56
  import { FixedJoint } from "../../engine-components/Joints.ts";
57
57
  import { FlyControls } from "../../engine-components/FlyControls.ts";
58
+ import { Fog } from "../../engine-components/Fog.ts";
58
59
  import { GltfExport } from "../../engine-components/export/gltf/GltfExport.ts";
59
60
  import { GltfExportBox } from "../../engine-components/export/gltf/GltfExport.ts";
60
61
  import { Gradient } from "../../engine-components/ParticleSystemModules.ts";
@@ -73,9 +74,6 @@ import { Keyboard } from "../../engine-components/ui/Keyboard.ts";
73
74
  import { LayoutGroup } from "../../engine-components/ui/Layout.ts";
74
75
  import { Light } from "../../engine-components/Light.ts";
75
76
  import { LimitVelocityOverLifetimeModule } from "../../engine-components/ParticleSystemModules.ts";
76
- import { LineInstanceHandler } from "../../engine-components-experimental/annotation/LinesManager.ts";
77
- import { LinesDrawer } from "../../engine-components-experimental/annotation/LineDrawer.ts";
78
- import { LinesManager } from "../../engine-components-experimental/annotation/LinesManager.ts";
79
77
  import { LODGroup } from "../../engine-components/LODGroup.ts";
80
78
  import { LODModel } from "../../engine-components/LODGroup.ts";
81
79
  import { LogStats } from "../../engine-components/debug/LogStats.ts";
@@ -172,7 +170,7 @@ import { XRGrabModel } from "../../engine-components/WebXRGrabRendering.ts";
172
170
  import { XRGrabRendering } from "../../engine-components/WebXRGrabRendering.ts";
173
171
  import { XRRig } from "../../engine-components/WebXRRig.ts";
174
172
  import { XRState } from "../../engine-components/XRFlag.ts";
175
-
173
+
176
174
  // Register types
177
175
  TypeStore.add("__Ignore", __Ignore);
178
176
  TypeStore.add("AlignmentConstraint", AlignmentConstraint);
@@ -228,6 +226,7 @@ TypeStore.add("EventTrigger", EventTrigger);
228
226
  TypeStore.add("FieldWithDefault", FieldWithDefault);
229
227
  TypeStore.add("FixedJoint", FixedJoint);
230
228
  TypeStore.add("FlyControls", FlyControls);
229
+ TypeStore.add("Fog", Fog);
231
230
  TypeStore.add("GltfExport", GltfExport);
232
231
  TypeStore.add("GltfExportBox", GltfExportBox);
233
232
  TypeStore.add("Gradient", Gradient);
@@ -246,9 +245,6 @@ TypeStore.add("Keyboard", Keyboard);
246
245
  TypeStore.add("LayoutGroup", LayoutGroup);
247
246
  TypeStore.add("Light", Light);
248
247
  TypeStore.add("LimitVelocityOverLifetimeModule", LimitVelocityOverLifetimeModule);
249
- TypeStore.add("LineInstanceHandler", LineInstanceHandler);
250
- TypeStore.add("LinesDrawer", LinesDrawer);
251
- TypeStore.add("LinesManager", LinesManager);
252
248
  TypeStore.add("LODGroup", LODGroup);
253
249
  TypeStore.add("LODModel", LODModel);
254
250
  TypeStore.add("LogStats", LogStats);
@@ -1,3 +1,5 @@
1
+ import "./engine_hot_reload"
2
+
1
3
  import * as layers from "./js-extensions/Layers";
2
4
  layers.patchLayers();
3
5
 
@@ -7,7 +9,7 @@ import "./tests/test_utils";
7
9
  import { RGBAColor } from "../engine-components/js-extensions/RGBAColor";
8
10
 
9
11
 
10
- const engine = {
12
+ const engine : any = {
11
13
  ...engine_setup,
12
14
  ...engine_scenetools,
13
15
  RGBAColor,
@@ -0,0 +1,186 @@
1
+ import { IComponent } from "./engine_types";
2
+ import { TypeStore } from "./engine_typestore";
3
+ import { addScriptToArrays, removeScriptFromContext } from "./engine_mainloop_utils"
4
+ import { showBalloonWarning } from "./debug/debug";
5
+ import { getParam } from "./engine_utils";
6
+
7
+ const debug = getParam("debughotreload");
8
+
9
+ declare type BeforeUpdateArgs = {
10
+ type: string,
11
+ updates: Array<{ path: string, timestamp: number, acceptedPath: string, explicitImportRequired: boolean, type: string }>,
12
+ }
13
+
14
+ //@ts-ignore
15
+ if (import.meta.hot) {
16
+ //@ts-ignore
17
+ import.meta.hot.on('vite:beforeUpdate', (cb: BeforeUpdateArgs) => {
18
+ if (debug) console.log(cb);
19
+ for (const update of cb.updates) {
20
+ console.log("[Needle Engine] Hot reloading " + update.path);
21
+ }
22
+ });
23
+ }
24
+
25
+
26
+ let isApplyingChanges = false;
27
+
28
+ const instances: Map<string, object[]> = new Map();
29
+
30
+ export function register(instance: object) {
31
+ if (isApplyingChanges) return;
32
+ const type = instance.constructor;
33
+ const name = type.name;
34
+ if (!instances.has(name)) {
35
+ instances.set(name, [instance]);
36
+ }
37
+ else {
38
+ instances.get(name)?.push(instance);
39
+ }
40
+ }
41
+
42
+ export function unregister(instance: object) {
43
+ if (isApplyingChanges) return;
44
+ const type = instance.constructor;
45
+ const name = type.name;
46
+ const instancesOfType = instances.get(name);
47
+ if (!instancesOfType) return;
48
+ const idx = instancesOfType.indexOf(instance);
49
+ if (idx === -1) return;
50
+ instancesOfType.splice(idx, 1);
51
+ }
52
+
53
+
54
+ let didRegisterUnhandledExceptionListener = false;
55
+ function reloadPageOnHotReloadError() {
56
+ if (debug) return;
57
+ if (didRegisterUnhandledExceptionListener) return;
58
+ didRegisterUnhandledExceptionListener = true;
59
+
60
+ const error = console.error;
61
+ console.error = (...args: any[]) => {
62
+ if (args.length) {
63
+ const arg: string = args[0];
64
+ // When making changes in e.g. the engine package and then making changes in project scripts again that import the engine package: hot reload fails and reports redefinitions of types, we just reload the page in those cases for now
65
+ // editing a script in one package seems to work for now so it should be good enough for a start
66
+ if (typeof arg === "string" && arg.includes("[hmr] Failed to reload ")) {
67
+ console.log("[Needle Engine] Hot reloading failed")
68
+ window.location.reload();
69
+ return;
70
+ }
71
+
72
+ }
73
+ error.apply(console, args);
74
+ };
75
+ }
76
+
77
+
78
+ export function applyChanges(newModule): boolean {
79
+
80
+ reloadPageOnHotReloadError();
81
+
82
+ // showBalloonMessage("Hot reloading");
83
+
84
+ // console.dir(newModule);
85
+
86
+ for (const key of Object.keys(newModule)) {
87
+ try {
88
+ isApplyingChanges = true;
89
+
90
+ const typeToUpdate = TypeStore.get(key);
91
+ if (!typeToUpdate)
92
+ {
93
+ continue;
94
+ }
95
+ const newType = newModule[key];
96
+
97
+ console.log("[Needle Engine] Updating type: " + key);
98
+
99
+ // Update prototype (methods and properties)
100
+ const previousMethods = Object.getOwnPropertyNames(typeToUpdate.prototype);
101
+ const methodsAndProperties = Object.getOwnPropertyDescriptors(newType.prototype);
102
+ for (const typeKey in methodsAndProperties) {
103
+ const desc = methodsAndProperties[typeKey];
104
+ if (!desc.writable) continue;
105
+ typeToUpdate.prototype[typeKey] = newModule[key].prototype[typeKey];
106
+ }
107
+ // Remove methods that are no longer present
108
+ for (const typeKey of previousMethods) {
109
+ if (!methodsAndProperties[typeKey]) {
110
+ delete typeToUpdate.prototype[typeKey];
111
+ }
112
+ }
113
+
114
+ // Update fields (we only add new fields if they are undefined)
115
+ // we create a instance to get access to the fields
116
+ const instancesOfType = instances.get(newType.name);
117
+ if (instancesOfType) {
118
+ const newTypeInstance = new newType();
119
+ const keys = Object.getOwnPropertyDescriptors(newTypeInstance);
120
+ for (const inst of instancesOfType) {
121
+ const componentInstance = inst as unknown as IComponent;
122
+ const isComponent = componentInstance.isComponent === true;
123
+ const active = isComponent ? componentInstance.activeAndEnabled : true;
124
+ const context = isComponent ? componentInstance.context : undefined;
125
+ try {
126
+ if (isComponent) {
127
+ removeScriptFromContext(componentInstance, context);
128
+ }
129
+ if (isComponent && active) {
130
+ componentInstance.enabled = false;
131
+ }
132
+
133
+ if (inst["onBeforeHotReloadFields"]) {
134
+ const res = inst["onBeforeHotReloadFields"]();
135
+ if (res === false) continue;
136
+ }
137
+ for (const key in keys) {
138
+ const desc = keys[key];
139
+ if (!desc.writable) continue;
140
+ if (inst[key] === undefined) {
141
+ inst[key] = newTypeInstance[key];
142
+ }
143
+ // if its a function but not on the prototype
144
+ // then its a bound method that needs to be rebound
145
+ else if (typeof inst[key] === "function" && !inst[key].prototype) {
146
+ const boundMethod = inst[key];
147
+ // try to get the target method name
148
+ const targetMethodName = boundMethod.name;
149
+ const prefix = "bound "; // < magic prefix
150
+ if (targetMethodName === prefix) continue;
151
+ const name = boundMethod.name.substring(prefix.length);
152
+ // if the target method name still exists on the new prototype
153
+ // we want to rebind it and assign it to the field
154
+ // Beware that this will not work if the method is added to some event listener etc
155
+ const newTarget = newType.prototype[name];
156
+ if (newTarget)
157
+ inst[key] = newTarget.bind(inst);
158
+ }
159
+ }
160
+ if (inst["onAfterHotReloadFields"]) inst["onAfterHotReloadFields"]();
161
+ }
162
+ finally {
163
+ if (isComponent) {
164
+ addScriptToArrays(componentInstance, context);
165
+ }
166
+ if (isComponent && active) {
167
+ componentInstance.enabled = true;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ catch (err) {
174
+ if (debug) console.error(err);
175
+ // we only want to invalidate changes if we debug hot reload
176
+ else return false;
177
+ }
178
+ finally {
179
+ isApplyingChanges = false;
180
+ }
181
+ }
182
+
183
+ return true;
184
+ }
185
+
186
+
@@ -170,7 +170,7 @@ export class Input extends EventTarget {
170
170
  *foreachPointerId(pointerType?: string | PointerType | string[] | PointerType[]): Generator<number> {
171
171
  for (let i = 0; i < this._pointerTypes.length; i++) {
172
172
  // check if the pointer is active
173
- if (this._pointerIsActive[i]) {
173
+ if (this._pointerIsActive(i)) {
174
174
  // if specific pointer types are requested
175
175
  if (pointerType !== undefined) {
176
176
  const type = this._pointerTypes[i];
@@ -95,13 +95,18 @@ async function handleLoadedGltf(context: Context, gltfId: string, gltf, seed: nu
95
95
  // findAnimationsLate(context, gltf, context.new_scripts_pre_setup_callbacks, false);
96
96
  }
97
97
 
98
+ export function createGLTFLoader(url: string, context: Context) {
99
+ const loader = new GLTFLoader();
100
+ const sourceId: SourceIdentifier = url;
101
+ registerExtensions(loader, context, sourceId);
102
+ return loader;
103
+ }
104
+
98
105
  export function parseSync(context: Context, data, path: string, seed: number | UIDProvider | null): Promise<GLTF | undefined> {
99
106
  if (typeof path !== "string") {
100
107
  console.warn("Parse gltf binary without path, this might lead to errors in resolving extensions. Please provide the source path of the gltf/glb file", path, typeof path);
101
108
  }
102
- const loader = new GLTFLoader();
103
- const sourceId: SourceIdentifier = path;
104
- registerExtensions(loader, context, sourceId);
109
+ const loader = createGLTFLoader(path, context);
105
110
  const componentsExtension = registerComponentExtension(loader);
106
111
  return new Promise((resolve, reject) => {
107
112
  try {
@@ -131,9 +136,7 @@ export function loadSync(context: Context, url: string, seed: number | UIDProvid
131
136
  // but due to the async nature and potentially triggering multiple loads at the same time
132
137
  // we need to make sure the extensions dont override each other
133
138
  // creating new loaders should not be expensive as well
134
- const loader = new GLTFLoader();
135
- const sourceId: SourceIdentifier = url;
136
- registerExtensions(loader, context, sourceId);
139
+ const loader = createGLTFLoader(url, context);
137
140
  const componentsExtension = registerComponentExtension(loader);
138
141
  return new Promise((resolve, reject) => {
139
142
  try {
@@ -1,4 +1,4 @@
1
- import { DepthTexture, PerspectiveCamera, WebGLRenderer, WebGLRenderTarget } from 'three'
1
+ import { Camera, DepthTexture, PerspectiveCamera, WebGLRenderer, WebGLRenderTarget } from 'three'
2
2
  import * as THREE from 'three'
3
3
  import { Input } from './engine_input';
4
4
  import { Physics } from './engine_physics';
@@ -300,7 +300,7 @@ export class Context {
300
300
 
301
301
  // private _requestSizeUpdate : boolean = false;
302
302
 
303
- private updateSize() {
303
+ updateSize() {
304
304
  if (!this.isManagedExternally && !this.renderer.xr.isPresenting) {
305
305
  this._sizeChanged = false;
306
306
  const scaleFactor = this.resolutionScaleFactor;
@@ -322,10 +322,12 @@ export class Context {
322
322
  }
323
323
  }
324
324
 
325
- updateAspect(camera: THREE.PerspectiveCamera) {
325
+ updateAspect(camera: THREE.PerspectiveCamera, width?: number, height?: number) {
326
326
  if (!camera) return;
327
- const width = this.domWidth;
328
- const height = this.domHeight;
327
+ if (width === undefined)
328
+ width = this.domWidth;
329
+ if (height === undefined)
330
+ height = this.domHeight;
329
331
  const pa = camera.aspect;
330
332
  camera.aspect = width / height;
331
333
  if (pa !== camera.aspect)
@@ -697,21 +699,13 @@ export class Context {
697
699
  }
698
700
  }
699
701
 
700
- this._currentFrameEvent = -10;
701
702
 
702
- this._isRendering = true;
703
- this.renderRequiredTextures();
704
703
  if (!this.isManagedExternally) {
705
- if (this.composer && !this.isInXR) {
706
- this.composer.render();
707
- }
708
- else if (this.mainCamera) {
709
- this.renderer.render(this.scene, this.mainCamera);
710
- }
704
+ this._currentFrameEvent = -10;
705
+ this.renderNow();
706
+ this._currentFrameEvent = FrameEvent.OnAfterRender;
711
707
  }
712
- this._isRendering = false;
713
708
 
714
- this._currentFrameEvent = FrameEvent.OnAfterRender;
715
709
 
716
710
  for (let i = 0; i < this.scripts_onAfterRender.length; i++) {
717
711
  const script = this.scripts_onAfterRender[i];
@@ -738,6 +732,23 @@ export class Context {
738
732
  this._stats?.end();
739
733
  }
740
734
 
735
+ renderNow(camera?: Camera) {
736
+ if (!camera) {
737
+ camera = this.mainCamera as Camera;
738
+ if (!camera) return false;
739
+ }
740
+ this._isRendering = true;
741
+ this.renderRequiredTextures();
742
+ if (this.composer && !this.isInXR) {
743
+ this.composer.render();
744
+ }
745
+ else if (this.mainCamera) {
746
+ this.renderer.render(this.scene, camera);
747
+ }
748
+ this._isRendering = false;
749
+ return true;
750
+ }
751
+
741
752
  /** returns true if we should return out of the frame loop */
742
753
  private _wasPaused: boolean = false;
743
754
  private onHandlePaused(): boolean {
@@ -798,47 +809,50 @@ export class Context {
798
809
  if (this.coroutines[evt]) {
799
810
  const evts = this.coroutines[evt];
800
811
  for (let i = 0; i < evts.length; i++) {
801
- const evt = evts[i];
802
- // TODO we might want to keep coroutines playing even if the component is disabled or inactive
803
- const remove = !evt.comp || evt.comp.destroyed || !evt.main || evt.comp["enabled"] === false;
804
- if (remove) {
805
- evts.splice(i, 1);
806
- --i;
807
- continue;
808
- }
809
- const iter = evt.chained;
810
- if (iter && iter.length > 0) {
811
- const last: Generator = iter[iter.length - 1];
812
- const res = last.next();
813
- if (res.done) {
814
- iter.pop();
812
+ try {
813
+ const evt = evts[i];
814
+ // TODO we might want to keep coroutines playing even if the component is disabled or inactive
815
+ const remove = !evt.comp || evt.comp.destroyed || !evt.main || evt.comp["enabled"] === false;
816
+ if (remove) {
817
+ evts.splice(i, 1);
818
+ --i;
819
+ continue;
815
820
  }
816
- if (isGenerator(res)) {
817
- if (!evt.chained) evt.chained = [];
818
- evt.chained.push(res.value);
821
+ const iter = evt.chained;
822
+ if (iter && iter.length > 0) {
823
+ const last: Generator = iter[iter.length - 1];
824
+ const res = last.next();
825
+ if (res.done) {
826
+ iter.pop();
827
+ }
828
+ if (isGenerator(res)) {
829
+ if (!evt.chained) evt.chained = [];
830
+ evt.chained.push(res.value);
831
+ }
832
+ if (!res.done) continue;
819
833
  }
820
- if (!res.done) continue;
821
- }
822
834
 
823
- const res = evt.main.next();
824
- if (res.done === true) {
825
- evts.splice(i, 1);
826
- --i;
827
- continue;
835
+ const res = evt.main.next();
836
+ if (res.done === true) {
837
+ evts.splice(i, 1);
838
+ --i;
839
+ continue;
840
+ }
841
+ const val = res.value;
842
+ if (isGenerator(val)) {
843
+ // invoke once if its a generator
844
+ // this means e.g. WaitForFrame(1) works and will capture
845
+ // the frame it was created
846
+ const gen = val as Generator;
847
+ const res = gen.next();
848
+ if (res.done) continue;
849
+ if (!evt.chained) evt.chained = [];
850
+ evt.chained.push(val as Generator);
851
+ }
828
852
  }
829
- const val = res.value;
830
- if (isGenerator(val)) {
831
- // invoke once if its a generator
832
- // this means e.g. WaitForFrame(1) works and will capture
833
- // the frame it was created
834
- const gen = val as Generator;
835
- const res = gen.next();
836
- if (res.done) continue;
837
- if (!evt.chained) evt.chained = [];
838
- evt.chained.push(val as Generator);
853
+ catch (e) {
854
+ console.error(e);
839
855
  }
840
-
841
-
842
856
  }
843
857
  }
844
858
 
@@ -1,6 +1,6 @@
1
1
  import * as THREE from "three";
2
2
  import { Mathf } from "./engine_math"
3
- import { WebGLRenderTarget, WebGLRenderer, Vector3, Quaternion, Uniform, Texture, Material, ShaderMaterial, CanvasTexture, AnimationAction } from "three";
3
+ import { WebGLRenderer, Vector3, Quaternion, Uniform, Texture, Material, ShaderMaterial, CanvasTexture, AnimationAction, Camera, PerspectiveCamera } from "three";
4
4
  import { CircularBuffer } from "./engine_utils";
5
5
 
6
6
 
@@ -183,8 +183,8 @@ export function logHierarchy(root: THREE.Object3D | null | undefined, collapsibl
183
183
  }
184
184
 
185
185
 
186
- export function isAnimationAction(obj:object){
187
- if(obj){
186
+ export function isAnimationAction(obj: object) {
187
+ if (obj) {
188
188
  // this doesnt work :(
189
189
  // return obj instanceof AnimationAction;
190
190
  // instead we do this:
@@ -308,4 +308,4 @@ function isImageBitmap(image) {
308
308
  (typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement) ||
309
309
  (typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas) ||
310
310
  (typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap);
311
- }
311
+ }
@@ -0,0 +1,41 @@
1
+ import { Context } from "./engine_setup";
2
+ import { PerspectiveCamera, Camera } from "three";
3
+
4
+ declare type ImageMimeType = "image/webp" | "image/png";
5
+
6
+ export function screenshot(context: Context, width: number, height: number, mimeType: ImageMimeType = "image/webp", camera?: Camera | null) {
7
+
8
+ if (!camera) {
9
+ camera = context.mainCamera;
10
+ if (!camera) {
11
+ console.error("No camera found");
12
+ return null;
13
+ }
14
+ }
15
+ const prevWidth = context.renderer.domElement.width;
16
+ const prevHeight = context.renderer.domElement.height;
17
+
18
+ try {
19
+ const canvas = context.renderer.domElement;
20
+
21
+ // set the desired output size
22
+ context.renderer.setSize(width, height);
23
+ // update the camera apsect and matrix
24
+ if (camera instanceof PerspectiveCamera)
25
+ context.updateAspect(camera, width, height);
26
+
27
+ // render now
28
+ context.renderNow();
29
+
30
+ // const webPMimeType = "image/webp";
31
+ // const pngMimeType = "image/png";
32
+ const dataUrl = canvas.toDataURL(mimeType);
33
+ return dataUrl;
34
+ }
35
+ finally {
36
+ context.renderer.setSize(prevWidth, prevHeight);
37
+ context.updateSize();
38
+ }
39
+
40
+ return null;
41
+ }
@@ -0,0 +1,60 @@
1
+ import { Behaviour } from "./Component";
2
+ import { Color, Fog as Fog3 } from "three";
3
+ import { serializable } from "../engine/engine_serialization";
4
+
5
+
6
+ export enum FogMode {
7
+
8
+ Linear = 1,
9
+ Exponential = 2,
10
+ ExponentialSquared = 3,
11
+ }
12
+
13
+ export class Fog extends Behaviour {
14
+
15
+ get fog() {
16
+ if (!this._fog) this._fog = new Fog3(0x000000, 0, 50);
17
+ return this._fog;
18
+ }
19
+
20
+ get mode() {
21
+ return FogMode.Linear;
22
+ }
23
+
24
+ @serializable()
25
+ set near(value: number) {
26
+ this.fog.near = value;
27
+ }
28
+ get near() {
29
+ return this.fog.near;
30
+ }
31
+
32
+ @serializable()
33
+ set far(value: number) {
34
+ this.fog.far = value;
35
+ }
36
+ get far() {
37
+ return this.fog.far;
38
+ }
39
+
40
+ @serializable(Color)
41
+ set color(value: Color) {
42
+ this.fog.color.copy(value);
43
+ }
44
+ get color() {
45
+ return this.fog.color;
46
+ }
47
+
48
+ private _fog?: Fog3;
49
+
50
+ onEnable() {
51
+ this.scene.fog = this.fog;
52
+ }
53
+
54
+ onDisable() {
55
+ if (this.scene.fog === this._fog)
56
+ this.scene.fog = null;
57
+ }
58
+
59
+
60
+ }
@@ -51,7 +51,8 @@ export class RemoteSkybox extends Behaviour {
51
51
  console.warn("Potentially invalid skybox url", this.url, "on", this.name);
52
52
  }
53
53
 
54
- if (!this._loader) {
54
+ // if (!this._loader)
55
+ {
55
56
  const isEXR = url.endsWith(".exr");
56
57
  const isHdr = url.endsWith(".hdr");
57
58
  if (isEXR) {
@@ -64,6 +65,7 @@ export class RemoteSkybox extends Behaviour {
64
65
  this._loader = new TextureLoader();
65
66
  }
66
67
  }
68
+ console.log("Loading skybox: " + url);
67
69
  const envMap = await this._loader.loadAsync(url);
68
70
  if (!envMap) return;
69
71
  if (!this.enabled) return;
@@ -3,7 +3,7 @@ import * as tests from "../engine/tests/test_utils";
3
3
  import { createTransformModel, SyncedTransform, SyncedTransformIdentifier } from "./SyncedTransform";
4
4
  import * as flatbuffers from 'flatbuffers';
5
5
  import { SyncedTransformModel } from "../engine-schemes/synced-transform-model";
6
- import { Rigidbody } from "./Rigidbody";
6
+ import { Rigidbody } from "./RigidBody";
7
7
  import { Vector3 } from "three";
8
8
  import { IModel } from "../engine/engine_networking_types";
9
9
 
@@ -221,14 +221,14 @@ export class WebXR extends Behaviour {
221
221
  this.context.domElement.append(buttonsContainer);
222
222
 
223
223
  // AR support
224
- if (this.enableAR && this.createARButton) {
224
+ if (this.enableAR && this.createARButton && arSupported) {
225
225
  arButton = WebXR.createARButton(this);
226
226
  this._arButton = arButton;
227
227
  buttonsContainer.appendChild(arButton);
228
228
  }
229
229
 
230
230
  // VR support
231
- if (this.createVRButton && this.enableVR) {
231
+ if (this.createVRButton && this.enableVR && vrSupported) {
232
232
  vrButton = WebXR.createVRButton(this);
233
233
  this._vrButton = vrButton;
234
234
  buttonsContainer.appendChild(vrButton);
@@ -53,6 +53,7 @@ export { EventTrigger } from "../EventTrigger";
53
53
  export { FieldWithDefault } from "../Renderer";
54
54
  export { FixedJoint } from "../Joints";
55
55
  export { FlyControls } from "../FlyControls";
56
+ export { Fog } from "../Fog";
56
57
  export { GltfExport } from "../export/gltf/GltfExport";
57
58
  export { GltfExportBox } from "../export/gltf/GltfExport";
58
59
  export { Gradient } from "../ParticleSystemModules";