Needle Engine

Changes between version 3.5.9-beta.2 and 3.5.10-beta
Files changed (13) hide show
  1. plugins/vite/config.js +8 -1
  2. plugins/vite/defines.js +1 -1
  3. plugins/vite/license.js +4 -1
  4. src/engine-components/CameraUtils.ts +7 -3
  5. src/engine-components/Component.ts +2 -2
  6. src/engine/debug/debug_overlay.ts +5 -2
  7. src/engine/engine_context.ts +11 -5
  8. src/engine/engine_element_loading.ts +9 -10
  9. src/engine/engine_element.ts +8 -3
  10. src/engine/engine_license.ts +5 -4
  11. src/engine/engine_physics_rapier.ts +33 -31
  12. src/engine-components/OrbitControls.ts +1 -1
  13. src/engine-components/SpriteRenderer.ts +13 -0
plugins/vite/config.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'fs';
2
2
 
3
+ let didLogCanNotFindConfig = false;
4
+
3
5
  /** the codegen meta file */
4
6
  export async function loadConfig(path) {
5
7
  try {
@@ -21,7 +23,12 @@
21
23
  const meta = JSON.parse(text);
22
24
  return meta;
23
25
  }
24
- else console.error("Could not find config file at " + path);
26
+ else {
27
+ if(!didLogCanNotFindConfig){
28
+ didLogCanNotFindConfig = true;
29
+ console.error("Could not find config file at " + path);
30
+ }
31
+ }
25
32
  return null;
26
33
  }
27
34
  catch (err) {
plugins/vite/defines.js CHANGED
@@ -27,7 +27,7 @@
27
27
  if (!viteConfig.define) viteConfig.define = {};
28
28
  viteConfig.define.NEEDLE_ENGINE_META = {
29
29
  version: tryGetNeedleEngineVersion(),
30
- generator: needleEngineConfig.generator,
30
+ generator: needleEngineConfig?.generator,
31
31
  }
32
32
 
33
33
  if (useRapier && userSettings?.useRapier !== true) {
plugins/vite/license.js CHANGED
@@ -32,7 +32,10 @@
32
32
  }
33
33
  }
34
34
  else {
35
- console.log("No config found - can not apply license");
35
+ if (!didLog) {
36
+ didLog = true;
37
+ console.log("No config found - can not apply license");
38
+ }
36
39
  }
37
40
  }
38
41
  }
src/engine-components/CameraUtils.ts CHANGED
@@ -16,14 +16,15 @@
16
16
  scene.add(cameraObject);
17
17
 
18
18
  const camInstance = new Camera();
19
- const cam = addNewComponent(cameraObject, camInstance, true) as ICamera
19
+ const cam = addNewComponent(cameraObject, camInstance, true) as ICamera;
20
20
  cam.sourceId = srcId;
21
21
  cam.clearFlags = 2;
22
22
  cam.backgroundColor = new RGBAColor(0.5, 0.5, 0.5, 1);
23
23
 
24
- cameraObject.position.x = -2;
25
- cameraObject.position.y = 2;
24
+ cameraObject.position.x = 0;
25
+ cameraObject.position.y = 1;
26
26
  cameraObject.position.z = 2;
27
+
27
28
  return cam;
28
29
  });
29
30
 
@@ -46,6 +47,9 @@
46
47
  if (cameraObject) {
47
48
  const orbit = addNewComponent(cameraObject, new OrbitControls(), false) as OrbitControls;
48
49
  orbit.sourceId = "unknown";
50
+ setTimeout(() => {
51
+ orbit.fitCameraToObjects(evt.context.scene.children, 1);
52
+ }, 100);
49
53
  }
50
54
  else {
51
55
  console.warn("Missing camera object, can not add orbit controls")
src/engine-components/Component.ts CHANGED
@@ -185,7 +185,7 @@
185
185
  * @param go component to move the component to
186
186
  * @param instance component to move to the GO
187
187
  */
188
- public static addComponent(go: IGameObject, instance: Component): void {
188
+ public static addComponent(go: IGameObject | Object3D, instance: Component): void {
189
189
  return this.moveComponent(go, instance);
190
190
  }
191
191
 
@@ -194,7 +194,7 @@
194
194
  * @param go component to move the component to
195
195
  * @param instance component to move to the GO
196
196
  */
197
- public static moveComponent(go: IGameObject, instance: Component): void {
197
+ public static moveComponent(go: IGameObject | Object3D, instance: Component): void {
198
198
  moveComponentInstance(go, instance as any);
199
199
  }
200
200
 
src/engine/debug/debug_overlay.ts CHANGED
@@ -104,7 +104,9 @@
104
104
 
105
105
  const currentMessages = new Set<string>();
106
106
 
107
- function showMessage(type: LogType, element: HTMLElement, msg: string) {
107
+ function showMessage(type: LogType, element: HTMLElement, msg: string | null | undefined) {
108
+ if (msg === null || msg === undefined) return;
109
+
108
110
  const container = getLogsContainer(element);
109
111
  if (container.childElementCount >= 20) {
110
112
  const last = container.lastElementChild;
@@ -117,8 +119,9 @@
117
119
  const msgcontainer = getMessageContainer(type, msg);
118
120
  container.prepend(msgcontainer);
119
121
 
122
+ const _msg = msg;
120
123
  setTimeout(() => {
121
- currentMessages.delete(msg);
124
+ currentMessages.delete(_msg);
122
125
  returnMessageContainer(msgcontainer);
123
126
  }, 10000);
124
127
  }
src/engine/engine_context.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  BufferGeometry, Camera, Clock, Color, DepthTexture, Group,
3
3
  Material, NearestFilter, NoToneMapping, Object3D, PCFSoftShadowMap,
4
4
  PerspectiveCamera, RGBAFormat, Scene, sRGBEncoding,
5
- Texture, WebGLRenderer, WebGLRenderTarget
5
+ Texture, WebGLRenderer, WebGLRendererParameters, WebGLRenderTarget
6
6
  } from 'three'
7
7
  import { Input } from './engine_input';
8
8
  import { Physics } from './engine_physics';
@@ -259,10 +259,16 @@
259
259
  this.isManagedExternally = true;
260
260
  }
261
261
  else {
262
- this.renderer = new WebGLRenderer({
263
- antialias: true
264
- });
262
+ const params: WebGLRendererParameters = {
263
+ antialias: true,
264
+ };
265
+
266
+ // get canvas already configured in the Needle Engine Web Component
267
+ const canvas = args?.domElement?.shadowRoot?.querySelector("canvas");
268
+ if (canvas) params.canvas = canvas;
265
269
 
270
+ this.renderer = new WebGLRenderer(params);
271
+
266
272
  // some tonemapping other than "NONE" is required for adjusting exposure with EXR environments
267
273
  this.renderer.toneMappingExposure = 1; // range [0...inf] instead of the usual -15..15
268
274
  this.renderer.toneMapping = NoToneMapping; // could also set to LinearToneMapping, ACESFilmicToneMapping
@@ -551,7 +557,7 @@
551
557
 
552
558
  // console.log(prepare_succeeded);
553
559
 
554
- if (!this.isManagedExternally)
560
+ if (!this.isManagedExternally && !this.domElement.shadowRoot)
555
561
  this.domElement.prepend(this.renderer.domElement);
556
562
 
557
563
  Context.Current = this;
src/engine/engine_element_loading.ts CHANGED
@@ -73,14 +73,15 @@
73
73
  }
74
74
 
75
75
  onLoadingBegin(message?: string) {
76
+ const _element = this._element.shadowRoot || this._element;
76
77
  if (debug) console.warn("Begin Loading")
77
78
  if (!this._loadingElement) {
78
- for (let i = 0; i < this._element.children.length; i++) {
79
- const el = this._element.children[i] as HTMLElement;
79
+ for (let i = 0; i < _element.children.length; i++) {
80
+ const el = _element.children[i] as HTMLElement;
80
81
  if (el.classList.contains(EngineLoadingView.LoadingContainerClassName)) {
81
82
  if (!this._allowCustomLoadingElement) {
82
83
  if (debug) console.warn("Remove custom loading container")
83
- this._element.removeChild(el);
84
+ _element.removeChild(el);
84
85
  continue;
85
86
  }
86
87
  this._loadingElement = this.createLoadingElement(el);
@@ -92,7 +93,7 @@
92
93
  this._progress = 0;
93
94
  this.loadingProgress = 0;
94
95
  this._loadingElement.style.display = "flex";
95
- this._element.appendChild(this._loadingElement);
96
+ _element.appendChild(this._loadingElement);
96
97
  this.smoothProgressLoop();
97
98
  this.setMessage(message ?? "");
98
99
  }
@@ -141,7 +142,7 @@
141
142
  if (typeof debugRendering === "number") dt *= debugRendering;
142
143
  }
143
144
  this._progressLoop = setInterval(() => {
144
- // increate loading speed when almost done
145
+ // increase loading speed when almost done
145
146
  if (this.loadingProgress >= .95 && !debugRendering) dt = .9;
146
147
  this._progress = Mathf.lerp(this._progress, this.loadingProgress, dt * this.loadingProgress);
147
148
  this.updateDisplay();
@@ -229,15 +230,12 @@
229
230
  else
230
231
  loadingBarContainer.style.backgroundColor = "rgba(255,255,255,.2)"
231
232
  // loadingBarContainer.style.alignItems = "center";
232
- this._loadingElement.appendChild(loadingBarContainer);
233
233
 
234
234
  const logo = document.createElement("img");
235
235
  const logoSize = 64;
236
236
  logo.style.width = `${logoSize}px`;
237
237
  logo.style.height = `${logoSize}px`;
238
- logo.style.position = "absolute";
239
- logo.style.left = "50%";
240
- logo.style.transform = `translate(-${logoSize * .5}px, -${logoSize + 32}px)`;
238
+ logo.style.marginBottom = "20px";
241
239
  logo.addEventListener("click", () => window.open("https://needle.tools", "_blank"));
242
240
  logo.style.cursor = "pointer";
243
241
  logo.style.userSelect = "none";
@@ -250,7 +248,8 @@
250
248
  logo.src = customLogo;
251
249
  }
252
250
  }
253
- loadingBarContainer.appendChild(logo);
251
+ this._loadingElement.appendChild(logo);
252
+ this._loadingElement.appendChild(loadingBarContainer);
254
253
 
255
254
 
256
255
  this._loadingBar = document.createElement("div");
src/engine/engine_element.ts CHANGED
@@ -276,18 +276,22 @@
276
276
  if (currentHash !== null && currentHash !== undefined)
277
277
  this._context.hash = currentHash;
278
278
  this._context.alias = alias;
279
- if (!this._context.isCreated)
279
+ const contextWasCreated = this._context.isCreated;
280
+ if (!contextWasCreated) {
280
281
  await this._context.onCreate(loadFunction);
282
+ }
281
283
  else {
282
284
  await loadFunction(this._context);
283
285
  }
284
286
 
285
287
 
286
288
 
287
-
289
+ console.log("Needle Engine: finished loading", alias ?? "")
288
290
  this._loadingProgress01 = 1;
289
- if (useDefaultLoading)
291
+ if (useDefaultLoading) {
290
292
  this._loadingView?.onLoadingUpdate(1, "creating scene");
293
+ if (contextWasCreated) this.onReady();
294
+ }
291
295
  this.classList.remove("loading");
292
296
  this.classList.add("loading-finished");
293
297
  if (debug)
@@ -301,6 +305,7 @@
301
305
 
302
306
  }
303
307
 
308
+ /** called by the context when the first frame has been rendered */
304
309
  private onReady = () => this._loadingView?.onLoadingFinished();
305
310
 
306
311
 
src/engine/engine_license.ts CHANGED
@@ -64,9 +64,10 @@
64
64
 
65
65
  const interval = setInterval(() => {
66
66
  if (!licenseElement) return;
67
- if (licenseElement.parentElement !== ctx.domElement) {
68
- ctx.domElement.appendChild(licenseElement);
69
- if (style) ctx.domElement.appendChild(style);
67
+ const parent = ctx.domElement.shadowRoot || ctx.domElement;
68
+ if (licenseElement.parentNode !== parent) {
69
+ parent.appendChild(licenseElement);
70
+ if (style) parent.appendChild(style);
70
71
  }
71
72
  }, 100);
72
73
 
@@ -121,7 +122,7 @@
121
122
  function createLicenseElement() {
122
123
  const licenseElement = document.createElement("div");
123
124
  licenseElement.setAttribute(licenseElementIdentifier, "");
124
- licenseElement.style.position = "fixed";
125
+ licenseElement.style.position = "absolute";
125
126
  licenseElement.style.bottom = "12px";
126
127
  licenseElement.style.right = "15px";
127
128
 
src/engine/engine_physics_rapier.ts CHANGED
@@ -21,6 +21,7 @@
21
21
  import { Mathf } from './engine_math';
22
22
  import { SphereOverlapResult } from './engine_types';
23
23
  import { ContextEvent, ContextRegistry } from './engine_context_registry';
24
+ import { isDevEnvironment } from './debug/debug';
24
25
 
25
26
  const debugPhysics = getParam("debugphysics");
26
27
  const debugColliderPlacement = getParam("debugphysicscolliders");
@@ -61,48 +62,49 @@
61
62
  export class RapierPhysics implements IPhysicsEngine {
62
63
 
63
64
  removeBody(obj: IComponent) {
65
+ if (!obj) return;
64
66
  this.validate();
65
67
  const body = obj[$bodyKey];
66
68
  obj[$bodyKey] = null;
67
69
  if (body && this.world) {
68
70
  const index = this.objects.findIndex(o => o === obj);
69
71
  if (index >= 0) {
70
- const body = this.bodies[index];
72
+ const rapierBody = this.bodies[index];
73
+ // Remove references
71
74
  this.bodies.splice(index, 1);
72
75
  this.objects.splice(index, 1);
73
76
 
74
- if (body instanceof Collider) {
75
- const collider = body as Collider;
76
- this.world?.removeCollider(collider, true);
77
+ // Remove the collider from the physics world
78
+ if (rapierBody instanceof Collider) {
79
+ const rapierCollider = rapierBody as Collider;
80
+ this.world?.removeCollider(rapierCollider, true);
77
81
 
78
- // remove the rigidbody if it doesnt have colliders anymore
79
- const rb = collider.parent();
80
- if (rb && rb.numColliders() <= 0) {
81
- this.world?.removeRigidBody(rb);
82
+ // also remove the rigidbody if it doesnt have colliders anymore
83
+ const rapierRigidbody: RigidBody | null = rapierCollider.parent();
84
+ if (rapierRigidbody && rapierRigidbody.numColliders() <= 0) {
85
+ const rigidbody = rapierRigidbody[$componentKey] as IRigidbody;
86
+ this.removeBody(rigidbody);
82
87
  }
83
88
  }
84
- else if (body instanceof RigidBody) {
85
- // TODO: running this code below causes a crash in rapier
86
- // const rb = body as RigidBody;
87
- // console.log("colliders", rb.numColliders())
88
- // for (let i = 0; i < rb.numColliders(); i++) {
89
- // const col = rb.collider(i);
90
- // this.world?.removeCollider(col, true);
91
- // }
92
- // console.log("colliders", rb.numColliders(), rb)
93
- // console.log(rb.handle, rb.userData);
94
- // if (rb.userData === undefined)
95
- // this.world?.removeRigidBody(rb);
89
+ // Remove the rigidbody from the physics world
90
+ else if (rapierBody instanceof RigidBody) {
91
+ if (rapierBody.numColliders() <= 0) {
92
+ this.world?.removeRigidBody(rapierBody);
93
+ }
94
+ else {
95
+ if (isDevEnvironment()) {
96
+ if (!rapierBody["did_log_removing"]) {
97
+ setTimeout(() => {
98
+ if (rapierBody.numColliders() > 0) {
99
+ rapierBody["did_log_removing"] = true;
100
+ console.warn("RapierPhysics: removing rigidbody with colliders from the physics world is not possible right now, please remove the colliders first");
101
+ }
102
+ }, 1);
103
+
104
+ }
105
+ }
106
+ }
96
107
  }
97
-
98
- // check if we need to remove the rigidbody too
99
- // const col = obj as ICollider;
100
- // if (col.isCollider && col.attachedRigidbody) {
101
- // const rb = col.attachedRigidbody[$bodyKey] as RigidBody;
102
- // if (rb && rb.numColliders() <= 0) {
103
- // // this.world?.removeRigidBody(rb);
104
- // }
105
- // }
106
108
  }
107
109
  }
108
110
  }
@@ -128,7 +130,7 @@
128
130
 
129
131
  updateProperties(rigidbody: IRigidbody) {
130
132
  this.validate();
131
- const physicsBody = rigidbody[$bodyKey];
133
+ const physicsBody = this.internal_getRigidbody(rigidbody);
132
134
  if (physicsBody) {
133
135
  this.internalUpdateProperties(rigidbody, physicsBody);
134
136
  }
@@ -206,7 +208,7 @@
206
208
 
207
209
  private async internalInitialization() {
208
210
  // NEEDLE_PHYSICS_INIT_START
209
- // use .env file with VITE_NEEDLE_USE_RAPIER=false to treeshape rapier
211
+ // use .env file with VITE_NEEDLE_USE_RAPIER=false to treeshake rapier
210
212
  // @ts-ignore
211
213
  if ("env" in import.meta && import.meta.env.VITE_NEEDLE_USE_RAPIER === "false") {
212
214
  return false;
src/engine-components/OrbitControls.ts CHANGED
@@ -87,7 +87,7 @@
87
87
  if (camGo && !this.setFromTargetPosition()) {
88
88
  if (this.debugLog)
89
89
  console.log("NO TARGET");
90
- const forward = new Vector3(0, 0, -1).applyMatrix4(camGo.cam.matrixWorld);
90
+ const forward = new Vector3(0, 0, -10).applyMatrix4(camGo.cam.matrixWorld);
91
91
  this.setTarget(forward, true);
92
92
  }
93
93
  }
src/engine-components/SpriteRenderer.ts CHANGED
@@ -161,6 +161,14 @@
161
161
  private _spriteSheet?: SpriteData;
162
162
  private _currentSprite?: THREE.Mesh;
163
163
 
164
+ // additional data
165
+ @serializable()
166
+ transparent: boolean = true;
167
+ @serializable()
168
+ cutoutThreshold: number = 0;
169
+ @serializable()
170
+ castShadows: boolean = false;
171
+
164
172
  awake(): void {
165
173
  this._currentSprite = undefined;
166
174
  if(debug) {
@@ -221,6 +229,11 @@
221
229
  this._currentSprite.layers.set(this.layer)
222
230
  }
223
231
 
232
+ if (this.sharedMaterial) {
233
+ this.sharedMaterial.alphaTest = this.cutoutThreshold;
234
+ this.sharedMaterial.transparent = this.transparent;
235
+ }
236
+ this._currentSprite.castShadow = this.castShadows;
224
237
  this._spriteSheet?.update();
225
238
  }
226
239
  }