@meimu/mutracer 0.1.0 → 0.1.2

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 (88) hide show
  1. package/README.md +61 -16
  2. package/dist/assets/bvh.worker.js +1 -1
  3. package/dist/mutracer.umd.js +668 -658
  4. package/docs/materials.md +27 -7
  5. package/docs/progressive-rendering.md +24 -8
  6. package/docs/threejs.md +15 -3
  7. package/docs/viewer.md +53 -0
  8. package/lib/Display.d.ts +1 -1
  9. package/lib/Display.js +4 -2
  10. package/lib/Display.js.map +1 -1
  11. package/lib/Render.d.ts +9 -0
  12. package/lib/Render.js +84 -13
  13. package/lib/Render.js.map +1 -1
  14. package/lib/RenderLoop.d.ts +5 -2
  15. package/lib/RenderLoop.js +47 -15
  16. package/lib/RenderLoop.js.map +1 -1
  17. package/lib/oidn.d.ts +1 -1
  18. package/lib/oidnBundledWeights.cdn.d.ts +2 -2
  19. package/lib/renderer/MaterialTable.d.ts +1 -0
  20. package/lib/renderer/MaterialTable.js +2 -1
  21. package/lib/renderer/MaterialTable.js.map +1 -1
  22. package/lib/renderer/WGPUBloomPass.js +8 -8
  23. package/lib/renderer/WGPUJNLMDenoiser.js +2 -2
  24. package/lib/renderer/WGPURayTraceOutput.js +5 -5
  25. package/lib/renderer/WGPURayTracePass.js +1 -1
  26. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  27. package/lib/renderer/WGPURenderingPipeline.js +8 -6
  28. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  29. package/lib/renderer/WebGLJNLMDenoiser.js +2 -2
  30. package/lib/renderer/WebGLRayTracePass.js +3 -1
  31. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  32. package/lib/renderer/committedScene.js +11 -6
  33. package/lib/renderer/committedScene.js.map +1 -1
  34. package/lib/renderer/glsl/chunks/envMap.glsl.js +5 -5
  35. package/lib/renderer/glsl/chunks/intersect.glsl.js +32 -32
  36. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +9 -9
  37. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +30 -30
  38. package/lib/renderer/glsl/chunks/rand.glsl.js +2 -2
  39. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +4 -4
  40. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +3 -3
  41. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +4 -4
  42. package/lib/renderer/glsl/chunks/sampling.glsl.js +3 -3
  43. package/lib/renderer/glsl/chunks/sobol.glsl.js +3 -3
  44. package/lib/renderer/glsl/rayTrace.frag.js +7 -3
  45. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  46. package/lib/renderer/glsl/webglPipeline.frag.js +2 -2
  47. package/lib/renderer/materialComparison.generated.js +5 -0
  48. package/lib/renderer/materialComparison.generated.js.map +1 -1
  49. package/lib/renderer/materialLayout.js +2 -1
  50. package/lib/renderer/materialLayout.js.map +1 -1
  51. package/lib/renderer/prepareMeshBVH.d.ts +4 -2
  52. package/lib/renderer/prepareMeshBVH.js +44 -20
  53. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  54. package/lib/renderer/util.d.ts +2 -2
  55. package/lib/renderer/util.js +3 -3
  56. package/lib/renderer/util.js.map +1 -1
  57. package/lib/renderer/wgsl/chunks/common.wgsl.js +2 -2
  58. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +14 -14
  59. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +85 -88
  60. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  61. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +34 -34
  62. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.d.ts +1 -1
  63. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +51 -46
  64. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js.map +1 -1
  65. package/lib/renderer/wgsl/chunks/rand.wgsl.js +2 -2
  66. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +3 -3
  67. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +4 -4
  68. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +4 -4
  69. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +3 -3
  70. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +3 -3
  71. package/lib/renderer/wgsl/compose.frag.wgsl.js +2 -2
  72. package/lib/renderer/wgsl/customDenoise.wgsl.js +4 -4
  73. package/lib/renderer/wgsl/offlineAdaptive.compute.js +10 -10
  74. package/lib/renderer/wgsl/rayTrace.compute.js +13 -9
  75. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  76. package/lib/scene/OpenPBRMaterial.d.ts +4 -2
  77. package/lib/scene/OpenPBRMaterial.js +4 -2
  78. package/lib/scene/OpenPBRMaterial.js.map +1 -1
  79. package/lib/scene/openPbrSchema.d.ts +7 -2
  80. package/lib/scene/openPbrSchema.js +2 -1
  81. package/lib/scene/openPbrSchema.js.map +1 -1
  82. package/lib/util/fromTHREE.d.ts +6 -0
  83. package/lib/util/fromTHREE.js +85 -19
  84. package/lib/util/fromTHREE.js.map +1 -1
  85. package/lib/version.d.ts +1 -1
  86. package/lib/version.js +1 -1
  87. package/lib/version.js.map +1 -1
  88. package/package.json +1 -1
@@ -42,8 +42,12 @@ export interface THREEControls {
42
42
  removeEventListener(type: 'start' | 'end', listener: () => void): void;
43
43
  target?: THREE.Vector3;
44
44
  enableDamping?: boolean;
45
+ /** Damping per 60 Hz frame; synchronization adjusts it for elapsed time. Default undefined uses the control's own timing. */
46
+ dampingFactor?: number;
45
47
  autoRotate?: boolean;
46
48
  enabled?: boolean;
49
+ /** Optional OrbitControls polar-angle accessor. Default undefined. */ getPolarAngle?(): number;
50
+ /** Optional OrbitControls azimuthal-angle accessor. Default undefined. */ getAzimuthalAngle?(): number;
47
51
  }
48
52
  /** Three.js sources synchronized on each tick. */
49
53
  export interface THREESyncOptions extends FromTHREESceneOptions {
@@ -69,8 +73,10 @@ export declare class THREESync extends Sync {
69
73
  private _store;
70
74
  private _disposed;
71
75
  private _started;
76
+ private _gestureChanged;
72
77
  private _cameraState;
73
78
  private _lastUpdate?;
79
+ private _idleControlsState?;
74
80
  private _start;
75
81
  private _end;
76
82
  /** Subscribes to controls without converting sources or modifying the engine. */
@@ -139,13 +139,19 @@ function convertMaterial(threeMaterial, textureCache) {
139
139
  material.emissiveMap = convertTexture(standardMaterial.emissiveMap, textureCache);
140
140
  material.normalMap = convertTexture(standardMaterial.normalMap, textureCache);
141
141
  material.displacementMap = convertTexture(standardMaterial.displacementMap, textureCache);
142
+ const displacementBias = Number(standardMaterial.displacementBias);
143
+ if (Number.isFinite(displacementBias))
144
+ material.displacementBias = displacementBias;
142
145
  const displacementScale = Number(standardMaterial.displacementScale);
143
- material.displacementScale = Number.isFinite(displacementScale)
146
+ // Three.js stores 1 even without a height map. Do not turn that inactive
147
+ // default into MuTracer's height when a texture is added after conversion.
148
+ material.displacementScale = Number.isFinite(displacementScale) &&
149
+ (standardMaterial.displacementMap != null || displacementScale !== 1)
144
150
  ? displacementScale
145
151
  : material.displacementScale;
146
152
  const displacementStepSize = Number(standardMaterial.userData.mutracerDisplacementStepSize);
147
153
  if (Number.isFinite(displacementStepSize)) {
148
- material.displacementStepSize = Math.max(0.000001, displacementStepSize);
154
+ material.displacementStepSize = Math.max(0, displacementStepSize);
149
155
  }
150
156
  const uvProjection = standardMaterial.userData
151
157
  .mutracerDisplacementUvProjection;
@@ -255,6 +261,12 @@ function convertMaterial(threeMaterial, textureCache) {
255
261
  : fallback;
256
262
  };
257
263
  if (openPbr) {
264
+ for (const key of ['displacementScale', 'displacementBias', 'displacementStepSize']) {
265
+ const value = openPbr[key];
266
+ if (typeof value === 'number' && Number.isFinite(value)) {
267
+ material[key] = key === 'displacementStepSize' ? Math.max(0, value) : value;
268
+ }
269
+ }
258
270
  if (openPbr.specularAnisotropyModel !== undefined) {
259
271
  specularAnisotropyModelIndex(openPbr.specularAnisotropyModel);
260
272
  material.specularAnisotropyModel = openPbr.specularAnisotropyModel;
@@ -936,6 +948,25 @@ function convertScene(scene, options, store) {
936
948
  lights
937
949
  };
938
950
  }
951
+ /** Inputs that can wake an idle OrbitControls instance, without inspecting its private inertia. */
952
+ function orbitControlsState(camera, controls) {
953
+ if (!camera || !controls.getPolarAngle || !controls.getAzimuthalAngle)
954
+ return;
955
+ camera.parent?.updateWorldMatrix(true, false);
956
+ const state = [camera, controls.update, camera.parent, ...camera.position.toArray(), ...camera.quaternion.toArray(),
957
+ ...camera.scale.toArray(), ...camera.up.toArray(), camera.zoom, ...(camera.parent?.matrixWorld.elements ?? [])];
958
+ for (const [key, value] of Object.entries(controls)) {
959
+ // Pointer-down changes the gesture state without changing the view.
960
+ if (key.startsWith('_') || key === 'state')
961
+ continue;
962
+ const vector = value;
963
+ if (vector?.isVector3)
964
+ state.push(key, ...vector.toArray());
965
+ else if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string')
966
+ state.push(key, value);
967
+ }
968
+ return state;
969
+ }
939
970
  /**
940
971
  * Keeps one MuTracer scene in sync with a Three.js scene, reusing unchanged objects.
941
972
  * Skinning/morph targets run on the CPU with per-source-mesh dynamic geometry.
@@ -953,9 +984,11 @@ export class THREESync extends Sync {
953
984
  _store = createConversionStore();
954
985
  _disposed = false;
955
986
  _started = false;
987
+ _gestureChanged = false;
956
988
  _cameraState = [];
957
989
  _lastUpdate;
958
- _start = () => { this._started = true; this.setActive(true); };
990
+ _idleControlsState;
991
+ _start = () => { this._started = true; this._gestureChanged = false; };
959
992
  _end = () => { this._started = false; };
960
993
  /** Subscribes to controls without converting sources or modifying the engine. */
961
994
  constructor(engine, _options = {}) {
@@ -971,25 +1004,55 @@ export class THREESync extends Sync {
971
1004
  if (this._disposed)
972
1005
  throw new Error('THREESync has been disposed');
973
1006
  const now = performance.now(), controls = this._options.controls, sourceCamera = this._options.camera;
974
- const dt = this._lastUpdate === undefined ? 0 : Math.min(.1, Math.max(0, (now - this._lastUpdate) / 1000));
1007
+ const elapsed = this._lastUpdate === undefined ? 0 : Math.max(0, (now - this._lastUpdate) / 1000);
1008
+ const dt = Math.min(.1, elapsed);
975
1009
  this._lastUpdate = now;
976
1010
  let moving = false, settled = false;
977
1011
  if (controls && this._options.updateControls !== false) {
978
- const position = sourceCamera?.position.clone(), zoom = sourceCamera?.zoom;
979
- moving = !!controls.update(dt);
980
- if (moving && !this._started && !controls.autoRotate && controls.enableDamping && position && sourceCamera && controls.target) {
981
- const step = position.distanceTo(sourceCamera.position);
982
- const zoomStep = Math.abs(Math.log(sourceCamera.zoom / zoom));
983
- if (step < 5e-4 * sourceCamera.position.distanceTo(controls.target) && zoomStep < 5e-4) {
984
- controls.enableDamping = false;
985
- try {
986
- controls.update(0);
987
- }
988
- finally {
989
- controls.enableDamping = true;
1012
+ const orbitInputs = orbitControlsState(sourceCamera, controls);
1013
+ // OrbitControls applies pointer movement before synchronization. Keep a
1014
+ // settled camera untouched on pointer-down until its inputs actually change.
1015
+ const idle = (!this._started || !this._gestureChanged) && !controls.autoRotate && orbitInputs && this._idleControlsState && snapshotsEqual(orbitInputs, this._idleControlsState);
1016
+ if (!idle) {
1017
+ this._idleControlsState = undefined;
1018
+ const position = sourceCamera?.position.clone(), rotation = sourceCamera?.quaternion.clone(), zoom = sourceCamera?.zoom;
1019
+ const target = controls.target?.clone(), damping = controls.dampingFactor;
1020
+ const frameDamping = controls.enableDamping && damping !== undefined && damping >= 0 && damping <= 1
1021
+ ? 1 - Math.pow(1 - damping, elapsed * 60) : undefined;
1022
+ if (frameDamping !== undefined)
1023
+ controls.dampingFactor = frameDamping;
1024
+ try {
1025
+ moving = !!controls.update(dt);
1026
+ }
1027
+ finally {
1028
+ if (frameDamping !== undefined)
1029
+ controls.dampingFactor = damping;
1030
+ }
1031
+ if (!this._started && !controls.autoRotate && controls.enableDamping && position && sourceCamera && controls.target) {
1032
+ const referenceScale = frameDamping ? damping / frameDamping : 1;
1033
+ const step = position.distanceTo(sourceCamera.position) * referenceScale;
1034
+ const zoomStep = Math.abs(Math.log(sourceCamera.zoom / zoom)) * referenceScale;
1035
+ const poseChanged = step > 0 || sourceCamera.zoom !== zoom || !!(orbitInputs && (!rotation.equals(sourceCamera.quaternion) || !target.equals(controls.target)));
1036
+ // End the remaining inertia once its 60 Hz movement is imperceptible.
1037
+ // update() can return false before that tail has actually stopped.
1038
+ if ((moving || poseChanged) && step < 5e-4 * sourceCamera.position.distanceTo(controls.target) && zoomStep < 5e-4) {
1039
+ controls.enableDamping = false;
1040
+ try {
1041
+ controls.update(0);
1042
+ }
1043
+ finally {
1044
+ controls.enableDamping = true;
1045
+ }
1046
+ moving = false;
1047
+ settled = true;
990
1048
  }
991
- moving = false;
992
- settled = true;
1049
+ // Once stopped, no more spherical/cartesian round trips occur until
1050
+ // a gesture, source pose, parent transform or public control option changes.
1051
+ if (orbitInputs && (settled || (!moving && !poseChanged)))
1052
+ this._idleControlsState = orbitControlsState(sourceCamera, controls);
1053
+ }
1054
+ else if (orbitInputs && !this._started && !controls.autoRotate && !moving) {
1055
+ this._idleControlsState = orbitControlsState(sourceCamera, controls);
993
1056
  }
994
1057
  }
995
1058
  }
@@ -1016,7 +1079,9 @@ export class THREESync extends Sync {
1016
1079
  this._camera.copy(next);
1017
1080
  this.engine.camera = this._camera;
1018
1081
  }
1019
- this.setActive(this._started || moving || this._store.poseChanged || (cameraChanged && !settled));
1082
+ if (this._started && (moving || cameraChanged))
1083
+ this._gestureChanged = true;
1084
+ this.setActive((this._started && this._gestureChanged) || moving || this._store.poseChanged || (cameraChanged && !settled));
1020
1085
  }
1021
1086
  /** Removes control subscriptions and caches. The last converted objects remain usable. */
1022
1087
  dispose() {
@@ -1025,6 +1090,7 @@ export class THREESync extends Sync {
1025
1090
  this._options.controls?.removeEventListener('start', this._start);
1026
1091
  this._options.controls?.removeEventListener('end', this._end);
1027
1092
  this._store = createConversionStore();
1093
+ this._idleControlsState = undefined;
1028
1094
  this._disposed = true;
1029
1095
  super.dispose();
1030
1096
  }