@needle-tools/engine 3.47.5-beta → 3.47.5-beta.1

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.
@@ -1,4 +1,4 @@
1
- import { Camera, Color, ColorRepresentation, PerspectiveCamera } from "three";
1
+ import { Camera, CanvasTexture, Color, ColorRepresentation, MirroredRepeatWrapping, PerspectiveCamera, RepeatWrapping, Texture, Vector2, WebGLRenderTarget } from "three";
2
2
 
3
3
  import { Renderer } from "../engine-components/Renderer.js";
4
4
  import { getComponentsInChildren } from "./engine_components.js";
@@ -64,19 +64,31 @@ export declare type ScreenshotOptions = {
64
64
  */
65
65
  background?: Color | RGBAColor | ColorRepresentation,
66
66
 
67
+ /**
68
+ * If true onBeforeRender and onAfterRender will be invoked on all renderers in the scene.
69
+ * @default true
70
+ */
71
+ render_events?: boolean,
72
+ };
73
+
74
+
75
+ export declare type ScreenshotOptionsDataUrl = ScreenshotOptions & {
67
76
  /**
68
77
  * If set the screenshot will be downloaded using the provided filename.
69
78
  * NOTE: if you need more control you can manually download the returned image using {@link saveImage}
70
79
  * @default undefined
71
80
  */
72
81
  download_filename?: string,
82
+ }
73
83
 
84
+ export declare type ScreenshotOptionsTexture = ScreenshotOptions & {
85
+ type: "texture",
74
86
  /**
75
- * If true onBeforeRender and onAfterRender will be invoked on all renderers in the scene.
76
- * @default true
87
+ * If set the screenshot will be saved to the provided texture.
88
+ * @default undefined
77
89
  */
78
- render_events?: boolean,
79
- };
90
+ target?: Texture,
91
+ }
80
92
 
81
93
  /**
82
94
  * Take a screenshot from the current scene.
@@ -93,15 +105,14 @@ export declare type ScreenshotOptions = {
93
105
  * saveImage(res, "screenshot.webp");
94
106
  * ```
95
107
  */
96
- export function screenshot2(opts: ScreenshotOptions = {
97
- mimeType: "image/png",
98
- transparent: false,
99
- }): string | null {
108
+ export function screenshot2(opts: ScreenshotOptionsTexture): Texture | null;
109
+ export function screenshot2(opts: ScreenshotOptionsDataUrl): string | null;
110
+ export function screenshot2(opts: ScreenshotOptionsDataUrl | ScreenshotOptionsTexture): Texture | string | null {
100
111
 
101
112
  if (!opts) opts = {}
102
113
 
114
+ const { mimeType = "image/png", transparent = false } = opts;
103
115
  let { context, width, height, camera } = opts;
104
- const { mimeType } = opts;
105
116
 
106
117
  if (!context) {
107
118
  context = ContextRegistry.Current as Context;
@@ -118,6 +129,7 @@ export function screenshot2(opts: ScreenshotOptions = {
118
129
  return null;
119
130
  }
120
131
  }
132
+
121
133
  const prevWidth = context.renderer.domElement.width;
122
134
  const prevHeight = context.renderer.domElement.height;
123
135
 
@@ -133,9 +145,11 @@ export function screenshot2(opts: ScreenshotOptions = {
133
145
  context.renderer.domElement.style.width = width + "px";
134
146
  context.renderer.domElement.style.height = height + "px";
135
147
 
148
+ const prevRenderTarget = context.renderer.getRenderTarget();
136
149
  const previousClearColor = context.renderer.getClearColor(new Color());
137
150
  const previousClearAlpha = context.renderer.getClearAlpha();
138
151
  const previousBackground = context.scene.background;
152
+ const previousAspect: number | null = "aspect" in camera ? camera.aspect : null;
139
153
 
140
154
  try {
141
155
 
@@ -150,7 +164,7 @@ export function screenshot2(opts: ScreenshotOptions = {
150
164
  renderers.forEach(r => r?.onBeforeRender());
151
165
  }
152
166
 
153
- if (opts.transparent) {
167
+ if (transparent) {
154
168
  context.scene.background = null;
155
169
  context.renderer.setClearColor(0x000000, 0);
156
170
  }
@@ -161,7 +175,7 @@ export function screenshot2(opts: ScreenshotOptions = {
161
175
  context.renderer.setClearAlpha(opts.background.a);
162
176
  }
163
177
  }
164
- if (opts.transparent) {
178
+ if (transparent) {
165
179
  context.renderer.setClearAlpha(0);
166
180
  }
167
181
 
@@ -174,8 +188,22 @@ export function screenshot2(opts: ScreenshotOptions = {
174
188
  camera = camera.cam;
175
189
  }
176
190
  // update the camera aspect and matrix
177
- if (camera instanceof PerspectiveCamera)
178
- context.updateAspect(camera, width, height);
191
+ if (camera instanceof PerspectiveCamera) {
192
+ camera.aspect = width / height;
193
+ camera.updateProjectionMatrix();
194
+ }
195
+
196
+ const textureOutput = "type" in opts && opts.type === "texture";
197
+ let targetTexture: WebGLRenderTarget | null = null;
198
+
199
+ if (textureOutput) {
200
+ targetTexture = new WebGLRenderTarget(width, height, {
201
+ wrapS: MirroredRepeatWrapping,
202
+ wrapT: MirroredRepeatWrapping,
203
+ format: 1023,
204
+ });
205
+ context.renderer.setRenderTarget(targetTexture);
206
+ }
179
207
 
180
208
  // render now
181
209
  context.renderNow(camera || null);
@@ -183,19 +211,42 @@ export function screenshot2(opts: ScreenshotOptions = {
183
211
  if (callRenderEvents)
184
212
  renderers.forEach(r => r.onAfterRender());
185
213
 
214
+ if ("type" in opts) {
215
+ if (opts.type === "texture") {
216
+ if (!targetTexture) {
217
+ console.error("No target texture found");
218
+ return null;
219
+ }
220
+ if (opts.target) {
221
+ opts.target.image = targetTexture?.texture.image;
222
+ opts.target.needsUpdate = true;
223
+ }
224
+ targetTexture.texture.offset.set(0, -1);
225
+ targetTexture.texture.needsUpdate = true;
226
+ return targetTexture.texture;
227
+ }
228
+ }
229
+
186
230
  const dataUrl = canvas.toDataURL(mimeType);
187
231
 
188
- if (opts.download_filename) {
232
+ if ("download_filename" in opts && opts.download_filename) {
189
233
  saveImage(dataUrl, opts.download_filename);
190
234
  }
191
235
 
192
236
  return dataUrl;
193
237
  }
194
238
  finally {
239
+ context.renderer.setRenderTarget(prevRenderTarget);
195
240
  context.scene.background = previousBackground;
196
241
  context.renderer.setSize(prevWidth, prevHeight, false);
197
- context.updateSize();
242
+ context.updateSize(true);
198
243
  context.renderer.setClearColor(previousClearColor, previousClearAlpha);
244
+ // Make sure to reset the aspect ratio. This is crucial if the main camera is not the currently active rendering camera
245
+ // For example if we did a screenshot from a different camera that has a different aspect ratio / fov
246
+ if (previousAspect != null && camera instanceof PerspectiveCamera) {
247
+ camera.aspect = previousAspect;
248
+ camera.updateProjectionMatrix();
249
+ }
199
250
  }
200
251
 
201
252
  return null;
@@ -86,9 +86,15 @@ function createDefaultCameraControls(context: IContext, cam?: ICamera) {
86
86
  const orbit = getOrAddComponent(cameraObject, OrbitControls) as OrbitControls;
87
87
  orbit.sourceId = cam?.sourceId ?? "unknown";
88
88
  const autoRotate = context.domElement.getAttribute("auto-rotate");
89
- orbit.autoRotate = autoRotate !== undefined && (autoRotate === "" || autoRotate === "true")
89
+ orbit.autoRotate = autoRotate !== undefined && autoRotate !== null && (autoRotate != "0" && autoRotate?.toLowerCase() != "false");
90
90
  orbit.autoRotateSpeed = 0.5;
91
91
  orbit.autoFit = true;
92
+ if (orbit.autoRotate && autoRotate) {
93
+ const autoRotateValue = parseFloat(autoRotate);
94
+ if (!isNaN(autoRotateValue)) {
95
+ orbit.autoRotateSpeed = autoRotateValue;
96
+ }
97
+ }
92
98
  }
93
99
  else {
94
100
  console.warn("Missing camera object, can not add orbit controls")
@@ -1,4 +1,4 @@
1
- import { Matrix4, Object3D, Quaternion, Vector3 } from "three";
1
+ import { Matrix4, Object3D, Quaternion, Vector3, Vector3Like } from "three";
2
2
 
3
3
  import { CollisionDetectionMode, RigidbodyConstraints } from "../engine/engine_physics.types.js";
4
4
  import { serializable } from "../engine/engine_serialization_decorator.js";
@@ -455,19 +455,30 @@ export class Rigidbody extends Behaviour implements IRigidbody {
455
455
  return this._currentVelocity;
456
456
  }
457
457
 
458
- public setAngularVelocity(x: number | Vector3, y?: number, z?: number, wakeup: boolean = true) {
459
- if (x instanceof Vector3) {
458
+ public setAngularVelocity(x: Vec3, wakeup?: boolean);
459
+ public setAngularVelocity(x: number, y: number, z: number, wakeup?: boolean);
460
+ public setAngularVelocity(x: number | Vec3, y?: number | boolean, z?: number, wakeup: boolean = true) {
461
+
462
+ if (typeof x === "object") {
460
463
  const vec = x;
461
464
  this.context.physics.engine?.setAngularVelocity(this, vec, wakeup);
462
465
  return;
463
466
  }
464
- if (y === undefined || z === undefined) return;
467
+ if (y === undefined || z === undefined || typeof y === "boolean") {
468
+ console.warn("setAngularVelocity expects either a Vec3 or 3 numbers");
469
+ return;
470
+ }
465
471
  this.context.physics.engine?.setAngularVelocity(this, { x: x, y: y, z: z }, wakeup);
466
472
  }
467
473
 
468
474
  /** Set the angular velocity of a rigidbody (equivalent to calling `setAngularVelocity`) */
469
- public setTorque(x: number | Vector3, y: number, z: number) {
470
- this.setAngularVelocity(x, y, z);
475
+ public setTorque(x: Vec3);
476
+ public setTorque(x: number, y: number, z: number);
477
+ public setTorque(x: number | Vec3, y?: number, z?: number) {
478
+ if (typeof x === "number") {
479
+ this.setAngularVelocity(x, y!, z!);
480
+ }
481
+ else this.setAngularVelocity(x);
471
482
  }
472
483
 
473
484
  /** Returns the rigidbody velocity smoothed over ~ 10 frames */