@bubo-squared/gyroview 0.1.0

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 (228) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +216 -0
  4. package/dist/define.d.ts +3 -0
  5. package/dist/define.js +4 -0
  6. package/dist/index.d.ts +959 -0
  7. package/dist/index.js +13 -0
  8. package/dist/packages/adapters/blob/src/BlobRandomAccessSource.js +37 -0
  9. package/dist/packages/adapters/blob/src/index.js +2 -0
  10. package/dist/packages/adapters/fetch/src/HttpRangeSource.js +194 -0
  11. package/dist/packages/adapters/fetch/src/HttpResourceLocator.js +35 -0
  12. package/dist/packages/adapters/fetch/src/httpRequest.js +126 -0
  13. package/dist/packages/adapters/fetch/src/index.js +3 -0
  14. package/dist/packages/adapters/mediabunny/src/MediabunnyAudioSegments.js +133 -0
  15. package/dist/packages/adapters/mediabunny/src/MediabunnyDemuxer.js +63 -0
  16. package/dist/packages/adapters/mediabunny/src/MediabunnyVideoTrackReader.js +84 -0
  17. package/dist/packages/adapters/mediabunny/src/SegmentChannel.js +84 -0
  18. package/dist/packages/adapters/mediabunny/src/bufferSources.js +12 -0
  19. package/dist/packages/adapters/mediabunny/src/index.js +2 -0
  20. package/dist/packages/adapters/mse-audio/src/MediaSourceAudioClock.js +139 -0
  21. package/dist/packages/adapters/mse-audio/src/SourceBufferFeeder.js +174 -0
  22. package/dist/packages/adapters/mse-audio/src/events.js +26 -0
  23. package/dist/packages/adapters/mse-audio/src/index.js +2 -0
  24. package/dist/packages/adapters/mse-audio/src/mediaSourceSupport.js +82 -0
  25. package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +274 -0
  26. package/dist/packages/adapters/three/src/compileAndProve.js +14 -0
  27. package/dist/packages/adapters/three/src/fullscreenPass.js +52 -0
  28. package/dist/packages/adapters/three/src/index.js +2 -0
  29. package/dist/packages/adapters/three/src/lensTextures.js +25 -0
  30. package/dist/packages/adapters/three/src/pictureMaterials.js +44 -0
  31. package/dist/packages/adapters/three/src/renderInto.js +15 -0
  32. package/dist/packages/adapters/three/src/rendererUniforms.js +157 -0
  33. package/dist/packages/adapters/three/src/samplingStrategies.js +26 -0
  34. package/dist/packages/adapters/three/src/seamMeter/SeamMeterPass.js +79 -0
  35. package/dist/packages/adapters/three/src/seamMeter/rowMeans.js +38 -0
  36. package/dist/packages/adapters/three/src/shaderPrograms.js +66 -0
  37. package/dist/packages/adapters/three/src/shaders/analysis.frag.js +4 -0
  38. package/dist/packages/adapters/three/src/shaders/equirectangularRays.js +4 -0
  39. package/dist/packages/adapters/three/src/shaders/fixedJoin.js +4 -0
  40. package/dist/packages/adapters/three/src/shaders/fullscreen.vert.js +4 -0
  41. package/dist/packages/adapters/three/src/shaders/header.js +4 -0
  42. package/dist/packages/adapters/three/src/shaders/lensModels.js +4 -0
  43. package/dist/packages/adapters/three/src/shaders/lensSampling.js +4 -0
  44. package/dist/packages/adapters/three/src/shaders/lensTextures.js +4 -0
  45. package/dist/packages/adapters/three/src/shaders/rawLenses.frag.js +4 -0
  46. package/dist/packages/adapters/three/src/shaders/rectilinearRays.js +4 -0
  47. package/dist/packages/adapters/three/src/shaders/screenAreas.js +4 -0
  48. package/dist/packages/adapters/three/src/shaders/stitch.frag.js +4 -0
  49. package/dist/packages/adapters/three/src/webglRenderer.js +37 -0
  50. package/dist/packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.js +135 -0
  51. package/dist/packages/adapters/webcodecs/src/index.js +2 -0
  52. package/dist/packages/core/src/application/gainMatching/GainMatching.js +66 -0
  53. package/dist/packages/core/src/application/gainMatching/GainMatchingFrameSink.js +62 -0
  54. package/dist/packages/core/src/application/playback/ClockWatch.js +76 -0
  55. package/dist/packages/core/src/application/playback/DecodePipeline.js +188 -0
  56. package/dist/packages/core/src/application/playback/DecodeRun.js +93 -0
  57. package/dist/packages/core/src/application/playback/FramePairQueue.js +93 -0
  58. package/dist/packages/core/src/application/playback/FramePairer.js +60 -0
  59. package/dist/packages/core/src/application/playback/PlaybackSession.js +417 -0
  60. package/dist/packages/core/src/application/playback/SeekOrder.js +36 -0
  61. package/dist/packages/core/src/application/playback/SessionLifecycle.js +42 -0
  62. package/dist/packages/core/src/application/playback/StartGate.js +55 -0
  63. package/dist/packages/core/src/application/playback/TimeUpdates.js +31 -0
  64. package/dist/packages/core/src/application/playback/WallClock.js +46 -0
  65. package/dist/packages/core/src/application/playback/keyframeTimeAt.js +15 -0
  66. package/dist/packages/core/src/application/playback/probeDecoding.js +168 -0
  67. package/dist/packages/core/src/application/recording/frameTimesOf.js +22 -0
  68. package/dist/packages/core/src/application/recording/inspectLayout.js +21 -0
  69. package/dist/packages/core/src/application/recording/inspectRecording.js +124 -0
  70. package/dist/packages/core/src/application/recording/locateOtherLensFile.js +18 -0
  71. package/dist/packages/core/src/application/recording/motionOf.js +39 -0
  72. package/dist/packages/core/src/application/recording/readRecording.js +31 -0
  73. package/dist/packages/core/src/application/recording/timeRecording.js +57 -0
  74. package/dist/packages/core/src/application/stabilization/StabilizingFrameSink.js +31 -0
  75. package/dist/packages/core/src/domain/format/boxes/BoxLayout.js +7 -0
  76. package/dist/packages/core/src/domain/format/boxes/boxConstants.js +16 -0
  77. package/dist/packages/core/src/domain/format/boxes/scanBoxes.js +59 -0
  78. package/dist/packages/core/src/domain/format/calibration/CalibrationVersion.js +13 -0
  79. package/dist/packages/core/src/domain/format/calibration/layouts/CalibrationStringLayout.js +34 -0
  80. package/dist/packages/core/src/domain/format/calibration/layouts/LegacyCalibrationLayout.js +44 -0
  81. package/dist/packages/core/src/domain/format/calibration/layouts/MeiCalibrationLayout.js +45 -0
  82. package/dist/packages/core/src/domain/format/calibration/layouts/PolynomialCalibrationLayout.js +44 -0
  83. package/dist/packages/core/src/domain/format/calibration/offsetTokens.js +65 -0
  84. package/dist/packages/core/src/domain/format/calibration/parseOffsetString.js +52 -0
  85. package/dist/packages/core/src/domain/format/calibration/selectCalibration.js +46 -0
  86. package/dist/packages/core/src/domain/format/captureOrigin.js +20 -0
  87. package/dist/packages/core/src/domain/format/constants.js +33 -0
  88. package/dist/packages/core/src/domain/format/info/infoFields.js +110 -0
  89. package/dist/packages/core/src/domain/format/info/parseInfoRecord.js +90 -0
  90. package/dist/packages/core/src/domain/format/info/readInfoRecord.js +14 -0
  91. package/dist/packages/core/src/domain/format/layout/detectLensLayout.js +122 -0
  92. package/dist/packages/core/src/domain/format/naming/RecordingFileName.js +67 -0
  93. package/dist/packages/core/src/domain/format/records/TrailerRecords.js +83 -0
  94. package/dist/packages/core/src/domain/format/records/exposure/parseExposureRecord.js +48 -0
  95. package/dist/packages/core/src/domain/format/records/gyro/FloatGyroSampleLayout.js +39 -0
  96. package/dist/packages/core/src/domain/format/records/gyro/RawGyroSampleLayout.js +55 -0
  97. package/dist/packages/core/src/domain/format/records/gyro/SampleColumns.js +82 -0
  98. package/dist/packages/core/src/domain/format/records/gyro/gyroLayouts.js +12 -0
  99. package/dist/packages/core/src/domain/format/records/gyro/parseGyroRecord.js +129 -0
  100. package/dist/packages/core/src/domain/format/trailer/RecordHeader.js +23 -0
  101. package/dist/packages/core/src/domain/format/trailer/Trailer.js +23 -0
  102. package/dist/packages/core/src/domain/format/trailer/TrailerFooter.js +27 -0
  103. package/dist/packages/core/src/domain/format/trailer/locateRecords.js +80 -0
  104. package/dist/packages/core/src/domain/format/trailer/parseRecordIndex.js +28 -0
  105. package/dist/packages/core/src/domain/format/trailer/readTrailer.js +32 -0
  106. package/dist/packages/core/src/domain/motion/exposure/ExposureRecord.js +53 -0
  107. package/dist/packages/core/src/domain/motion/gyro/GyroTrack.js +60 -0
  108. package/dist/packages/core/src/domain/motion/gyro/medianStep.js +11 -0
  109. package/dist/packages/core/src/domain/motion/gyro/repairedTimeline.js +101 -0
  110. package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +112 -0
  111. package/dist/packages/core/src/domain/motion/orientation/OrientationTrack.js +146 -0
  112. package/dist/packages/core/src/domain/motion/orientation/estimateGyroBias.js +64 -0
  113. package/dist/packages/core/src/domain/motion/orientation/gravity.js +97 -0
  114. package/dist/packages/core/src/domain/motion/stabilization/Stabilizer.js +13 -0
  115. package/dist/packages/core/src/domain/motion/stabilization/stabilizers.js +86 -0
  116. package/dist/packages/core/src/domain/motion/timing/CaptureClock.js +41 -0
  117. package/dist/packages/core/src/domain/motion/timing/FrameTimes.js +92 -0
  118. package/dist/packages/core/src/domain/motion/timing/frameTimeSources.js +61 -0
  119. package/dist/packages/core/src/domain/motion/timing/resolveFrameTimes.js +68 -0
  120. package/dist/packages/core/src/domain/optics/EquidistantModel.js +39 -0
  121. package/dist/packages/core/src/domain/optics/MeiModel.js +45 -0
  122. package/dist/packages/core/src/domain/optics/PolynomialModel.js +44 -0
  123. package/dist/packages/core/src/domain/optics/gainMatch.js +62 -0
  124. package/dist/packages/core/src/domain/optics/lensGeometry.js +18 -0
  125. package/dist/packages/core/src/domain/optics/lensPose.js +35 -0
  126. package/dist/packages/core/src/domain/optics/opticsConstants.js +5 -0
  127. package/dist/packages/core/src/domain/playback/PlayerState.js +83 -0
  128. package/dist/packages/core/src/domain/stitching/LensLayout.js +21 -0
  129. package/dist/packages/core/src/domain/stitching/StitchingSetup.js +78 -0
  130. package/dist/packages/core/src/domain/stitching/seamDisparity.js +4 -0
  131. package/dist/packages/core/src/domain/stitching/seamDisparityField.js +5 -0
  132. package/dist/packages/core/src/domain/stitching/seamJoin.js +16 -0
  133. package/dist/packages/core/src/domain/stitching/seamMismatch.js +1 -0
  134. package/dist/packages/core/src/domain/stitching/seamRing.js +4 -0
  135. package/dist/packages/core/src/domain/stitching/seamStrip.js +26 -0
  136. package/dist/packages/core/src/domain/view/Framing.js +31 -0
  137. package/dist/packages/core/src/domain/view/PictureQuality.js +26 -0
  138. package/dist/packages/core/src/domain/view/ViewMode.js +9 -0
  139. package/dist/packages/core/src/domain/view/ViewState.js +48 -0
  140. package/dist/packages/core/src/domain/view/lensTilesView.js +63 -0
  141. package/dist/packages/core/src/domain/view/magnification.js +103 -0
  142. package/dist/packages/core/src/domain/view/normalView.js +29 -0
  143. package/dist/packages/core/src/domain/view/panoramaView.js +99 -0
  144. package/dist/packages/core/src/domain/view/rectilinear.js +40 -0
  145. package/dist/packages/core/src/domain/view/screenLayout.js +113 -0
  146. package/dist/packages/core/src/domain/view/viewGestures.js +98 -0
  147. package/dist/packages/core/src/domain/view/viewModes.js +14 -0
  148. package/dist/packages/core/src/index.js +59 -0
  149. package/dist/packages/core/src/ports/FramePair.js +6 -0
  150. package/dist/packages/core/src/shared/async/Deferred.js +42 -0
  151. package/dist/packages/core/src/shared/async/RunStop.js +40 -0
  152. package/dist/packages/core/src/shared/binary/ByteRange.js +38 -0
  153. package/dist/packages/core/src/shared/binary/ByteReader.js +77 -0
  154. package/dist/packages/core/src/shared/errors/GyroViewError.js +81 -0
  155. package/dist/packages/core/src/shared/errors/reportLater.js +11 -0
  156. package/dist/packages/core/src/shared/events/Outbox.js +68 -0
  157. package/dist/packages/core/src/shared/events/TypedEmitter.js +55 -0
  158. package/dist/packages/core/src/shared/keysOf.js +10 -0
  159. package/dist/packages/core/src/shared/lazy.js +11 -0
  160. package/dist/packages/core/src/shared/math/Matrix3.js +93 -0
  161. package/dist/packages/core/src/shared/math/Quaternion.js +133 -0
  162. package/dist/packages/core/src/shared/math/Vector3.js +54 -0
  163. package/dist/packages/core/src/shared/math/clamp.js +9 -0
  164. package/dist/packages/core/src/shared/protobuf/ProtobufMessage.js +157 -0
  165. package/dist/packages/core/src/shared/text/urlPath.js +31 -0
  166. package/dist/packages/core/src/shared/text/utf8.js +12 -0
  167. package/dist/packages/core/src/shared/units/angle.js +22 -0
  168. package/dist/packages/core/src/shared/units/time.js +13 -0
  169. package/dist/packages/player/src/PlayerSource.js +15 -0
  170. package/dist/packages/player/src/browserPlayer.js +18 -0
  171. package/dist/packages/player/src/choices.js +25 -0
  172. package/dist/packages/player/src/composition/Disposables.js +31 -0
  173. package/dist/packages/player/src/composition/browserPorts.js +46 -0
  174. package/dist/packages/player/src/composition/buildPipeline.js +153 -0
  175. package/dist/packages/player/src/composition/ensureDecodable.js +63 -0
  176. package/dist/packages/player/src/composition/openInputs.js +179 -0
  177. package/dist/packages/player/src/composition/openRecording.js +42 -0
  178. package/dist/packages/player/src/controls/ChoiceMenu.js +113 -0
  179. package/dist/packages/player/src/controls/PictureMenus.js +47 -0
  180. package/dist/packages/player/src/controls/SeekBar.js +125 -0
  181. package/dist/packages/player/src/controls/SoundControls.js +41 -0
  182. package/dist/packages/player/src/controls/TransportButtons.js +31 -0
  183. package/dist/packages/player/src/controls/ViewGestures.js +136 -0
  184. package/dist/packages/player/src/controls/Wording.js +78 -0
  185. package/dist/packages/player/src/controls/controlParts.js +36 -0
  186. package/dist/packages/player/src/controls/controls.js +4 -0
  187. package/dist/packages/player/src/controls/controlsBar.js +23 -0
  188. package/dist/packages/player/src/controls/controlsMarkup.js +85 -0
  189. package/dist/packages/player/src/controls/customControls.js +59 -0
  190. package/dist/packages/player/src/controls/formatTime.js +19 -0
  191. package/dist/packages/player/src/controls/icons.js +40 -0
  192. package/dist/packages/player/src/controls/keyboard.js +118 -0
  193. package/dist/packages/player/src/controls/messages.js +94 -0
  194. package/dist/packages/player/src/controls/parseMarkup.js +31 -0
  195. package/dist/packages/player/src/controls/viewButtons.js +14 -0
  196. package/dist/packages/player/src/element/ElementLoads.js +119 -0
  197. package/dist/packages/player/src/element/FullscreenToggle.js +39 -0
  198. package/dist/packages/player/src/element/GyroViewElement.js +313 -0
  199. package/dist/packages/player/src/element/IdleWatcher.js +71 -0
  200. package/dist/packages/player/src/element/TypedEventElement.js +22 -0
  201. package/dist/packages/player/src/element/accessibleRegion.js +19 -0
  202. package/dist/packages/player/src/element/attributeNames.js +51 -0
  203. package/dist/packages/player/src/element/attributes.js +101 -0
  204. package/dist/packages/player/src/element/defineGyroView.js +21 -0
  205. package/dist/packages/player/src/element/earlyProperties.js +36 -0
  206. package/dist/packages/player/src/element/elementSource.js +23 -0
  207. package/dist/packages/player/src/element/liveSettings.js +138 -0
  208. package/dist/packages/player/src/element/playbackAttributes.js +53 -0
  209. package/dist/packages/player/src/element/playerEventMirror.js +33 -0
  210. package/dist/packages/player/src/element/reflectedProperties.js +78 -0
  211. package/dist/packages/player/src/element/styles.js +4 -0
  212. package/dist/packages/player/src/element/template.js +47 -0
  213. package/dist/packages/player/src/index.js +10 -0
  214. package/dist/packages/player/src/inspectRecording.js +22 -0
  215. package/dist/packages/player/src/player/DrawingBufferFit.js +77 -0
  216. package/dist/packages/player/src/player/FrameLoop.js +40 -0
  217. package/dist/packages/player/src/player/PictureSettings.js +60 -0
  218. package/dist/packages/player/src/player/Player.js +377 -0
  219. package/dist/packages/player/src/player/PlayerEvents.js +28 -0
  220. package/dist/packages/player/src/player/PlayerPhase.js +21 -0
  221. package/dist/packages/player/src/player/PlayerSound.js +61 -0
  222. package/dist/packages/player/src/player/PlayerView.js +113 -0
  223. package/dist/packages/player/src/player/SessionRelay.js +66 -0
  224. package/dist/packages/player/src/player/ensureFinite.js +26 -0
  225. package/dist/packages/player/src/player/loadRecording.js +65 -0
  226. package/dist/packages/player/src/player/transportEvents.js +29 -0
  227. package/dist/standalone.js +4919 -0
  228. package/package.json +73 -0
@@ -0,0 +1,274 @@
1
+ import { GyroViewError, ensureInvariant } from "../../../core/src/shared/errors/GyroViewError.js";
2
+ import { DEFAULT_PICTURE_QUALITY } from "../../../core/src/domain/view/PictureQuality.js";
3
+ import { DEFAULT_VIEW_MODE } from "../../../core/src/domain/view/ViewMode.js";
4
+ import { aspectOf } from "../../../core/src/domain/view/screenLayout.js";
5
+ import { DEFAULT_FRAMING } from "../../../core/src/domain/view/Framing.js";
6
+ import { viewModeRulesFor } from "../../../core/src/domain/view/viewModes.js";
7
+ import "../../../core/src/index.js";
8
+ import { SAMPLING_STRATEGIES } from "./samplingStrategies.js";
9
+ import { applyLensGain, applyPicture, applyShaderSampling, applyStabilization, createRendererUniforms } from "./rendererUniforms.js";
10
+ import { createFullscreenTriangle } from "./fullscreenPass.js";
11
+ import { createRenderer } from "./webglRenderer.js";
12
+ import { SeamMeterPass } from "./seamMeter/SeamMeterPass.js";
13
+ import { applyTextureFilters, createLensTextures } from "./lensTextures.js";
14
+ import { PLAYER_SHADERS, compilePictureMaterials, createPictureMaterials, disposePictureMaterials } from "./pictureMaterials.js";
15
+ import { Camera, Mesh, Scene } from "three";
16
+ //#region ../../packages/adapters/three/src/ThreeFrameRenderer.ts
17
+ var PLAYER_PICTURES = {
18
+ shaders: PLAYER_SHADERS,
19
+ uniforms: {}
20
+ };
21
+ /**
22
+ * PictureRenderer over Three.js: uploads each lens frame to a texture and draws one fullscreen pass
23
+ * per frame, laid out on the viewport as the view mode's picture for the framing says: the
24
+ * stitched sphere through `stitch.frag.glsl`, turned by the view and stabilization rotations, or
25
+ * each lens image in a tile of its own.
26
+ */
27
+ var ThreeFrameRenderer = class {
28
+ parts;
29
+ canvas;
30
+ framing = DEFAULT_FRAMING;
31
+ viewMode = DEFAULT_VIEW_MODE;
32
+ /**
33
+ * False until the first pair arrives. The textures then hold the pair on screen, which the
34
+ * session keeps open until it presents the next one.
35
+ */
36
+ hasFrames = false;
37
+ isDisposed = false;
38
+ meters = /* @__PURE__ */ new Set();
39
+ constructor(parts, canvas) {
40
+ this.parts = parts;
41
+ this.canvas = canvas;
42
+ this.applyFraming();
43
+ canvas.addEventListener("webglcontextrestored", this.onContextRestored);
44
+ }
45
+ static create(canvas, setup, options = {}) {
46
+ const opening = {
47
+ options,
48
+ pictures: PLAYER_PICTURES
49
+ };
50
+ return new this(this.openParts(canvas, setup, opening), canvas);
51
+ }
52
+ /**
53
+ * The context, textures, uniforms and compiled programs of a renderer that draws with the
54
+ * given pictures.
55
+ */
56
+ static openParts(canvas, setup, opening) {
57
+ ensureDrawable(setup);
58
+ const renderer = createRenderer(canvas, opening.options);
59
+ try {
60
+ return assembleParts(renderer, setup, opening.pictures);
61
+ } catch (error) {
62
+ renderer.dispose();
63
+ throw error;
64
+ }
65
+ }
66
+ get lensCount() {
67
+ return this.parts.lensCount;
68
+ }
69
+ setFraming(framing) {
70
+ this.ensureLive();
71
+ this.framing = framing;
72
+ this.applyFraming();
73
+ this.render();
74
+ }
75
+ setViewMode(mode) {
76
+ this.ensureLive();
77
+ this.viewMode = mode;
78
+ this.applyFraming();
79
+ this.render();
80
+ }
81
+ /**
82
+ * The frames standing on screen are uploaded again with the new filters and mip chain, so the
83
+ * change shows at once, even while paused.
84
+ */
85
+ setQuality(quality) {
86
+ this.ensureLive();
87
+ const strategy = SAMPLING_STRATEGIES[quality];
88
+ for (const texture of this.parts.textures) {
89
+ applyTextureFilters(texture, strategy);
90
+ if (this.hasFrames) texture.needsUpdate = true;
91
+ }
92
+ applyShaderSampling(this.parts.uniforms, strategy);
93
+ this.render();
94
+ }
95
+ setLensGains(gains) {
96
+ this.ensureLive();
97
+ for (const [lensIndex, gain] of gains.entries()) applyLensGain(this.parts.uniforms, lensIndex, gain);
98
+ this.render();
99
+ }
100
+ /**
101
+ * The meter leaves this renderer's list when disposed; the renderer disposes those still live
102
+ * when it goes, since they share its context and uniforms.
103
+ */
104
+ createSeamMeter() {
105
+ this.ensureLive();
106
+ const pass = new SeamMeterPass(this.parts.renderer, this.parts.uniforms, this.parts.lensCount);
107
+ return this.tracked({
108
+ measure: () => pass.measure(),
109
+ dispose: () => {
110
+ pass.dispose();
111
+ }
112
+ });
113
+ }
114
+ /**
115
+ * Turns the whole picture: the stabilized frame the viewer looks around in, into the body.
116
+ * Does not redraw by itself: the stabilizing sink calls it right before presenting a pair.
117
+ */
118
+ setStabilization(rotation) {
119
+ this.ensureLive();
120
+ applyStabilization(this.parts.uniforms, rotation);
121
+ }
122
+ present(presentation) {
123
+ this.ensureLive();
124
+ const { frames } = presentation.pair;
125
+ ensureInvariant(frames.length === this.parts.textures.length, `a pair of ${frames.length} frames does not fit ${this.parts.textures.length} lens textures`);
126
+ for (const [index, texture] of this.parts.textures.entries()) {
127
+ const frame = frames[index];
128
+ if (frame) texture.setFrame(frame.handle);
129
+ }
130
+ this.hasFrames = true;
131
+ this.render();
132
+ }
133
+ /**
134
+ * Matches the drawing buffer to a new element size, in device pixels.
135
+ */
136
+ resize(size) {
137
+ this.ensureLive();
138
+ this.parts.renderer.setSize(size.width, size.height, false);
139
+ this.applyFraming();
140
+ this.render();
141
+ }
142
+ dispose() {
143
+ if (this.isDisposed) return;
144
+ this.isDisposed = true;
145
+ this.canvas.removeEventListener("webglcontextrestored", this.onContextRestored);
146
+ for (const meter of this.meters) meter.dispose();
147
+ this.parts.seamProof.dispose();
148
+ for (const texture of this.parts.textures) texture.dispose();
149
+ disposePictureMaterials(this.parts.materials);
150
+ this.parts.pass.geometry.dispose();
151
+ this.parts.renderer.dispose();
152
+ }
153
+ /**
154
+ * The WebGL renderer and the shared uniforms, for a subclass's own passes and settings.
155
+ */
156
+ get webgl() {
157
+ return this.parts.renderer;
158
+ }
159
+ get uniforms() {
160
+ return this.parts.uniforms;
161
+ }
162
+ /**
163
+ * A meter over a pass that shares this renderer's context, which the renderer disposes with
164
+ * itself unless it is disposed first.
165
+ */
166
+ tracked(meter) {
167
+ const kept = {
168
+ ...meter,
169
+ dispose: () => {
170
+ this.meters.delete(kept);
171
+ meter.dispose();
172
+ }
173
+ };
174
+ this.meters.add(kept);
175
+ return kept;
176
+ }
177
+ render() {
178
+ if (!this.hasFrames) return;
179
+ this.parts.renderer.render(this.parts.scene, this.parts.camera);
180
+ }
181
+ ensureLive() {
182
+ ensureInvariant(!this.isDisposed, "the renderer has been disposed");
183
+ }
184
+ /**
185
+ * Three.js builds its state anew on a restored context but draws nothing: a paused picture
186
+ * (iOS drops the context of a tab in the background) comes back only through a redraw, which
187
+ * uploads the frames on screen again.
188
+ */
189
+ onContextRestored = () => {
190
+ this.render();
191
+ };
192
+ /**
193
+ * Asks the view mode what to draw for the framing on this canvas, and switches to that
194
+ * picture's program.
195
+ */
196
+ applyFraming() {
197
+ const viewport = {
198
+ width: this.canvas.width,
199
+ height: this.canvas.height
200
+ };
201
+ const picture = viewModeRulesFor(this.viewMode).picture(this.framing, {
202
+ viewport,
203
+ lensCount: this.parts.lensCount
204
+ });
205
+ this.parts.pass.material = this.parts.materials[picture.kind];
206
+ applyPicture(this.parts.uniforms, picture, aspectOf(viewport));
207
+ }
208
+ };
209
+ /**
210
+ * The shader has room for `MAX_LENSES` lenses from `LENS_TEXTURES` decoded frames; a layout
211
+ * beyond either would be drawn wrong without a word, so it is refused.
212
+ */
213
+ function ensureDrawable(setup) {
214
+ if (setup.lenses.length <= 2 && setup.frameSlotCount <= 2) return;
215
+ throw new GyroViewError("unsupported-layout", `the renderer draws at most 2 lenses from 2 frames; the layout has ${setup.lenses.length} lenses in ${setup.frameSlotCount} frames`);
216
+ }
217
+ /**
218
+ * The textures, uniforms and programs every picture shares, read at the default quality.
219
+ */
220
+ function sharedParts(setup, pictures) {
221
+ const strategy = SAMPLING_STRATEGIES[DEFAULT_PICTURE_QUALITY];
222
+ const textures = createLensTextures(setup.frameSlotCount, strategy);
223
+ const uniforms = createRendererUniforms(setup, textures);
224
+ applyShaderSampling(uniforms, strategy);
225
+ return {
226
+ textures,
227
+ uniforms,
228
+ materials: createPictureMaterials({
229
+ ...uniforms,
230
+ ...pictures.uniforms
231
+ }, pictures.shaders)
232
+ };
233
+ }
234
+ function assembleParts(renderer, setup, pictures) {
235
+ const { textures, uniforms, materials } = sharedParts(setup, pictures);
236
+ const pass = new Mesh(createFullscreenTriangle(), materials.rectilinear);
237
+ const seamProof = provenSeamMeter(renderer, {
238
+ pass,
239
+ materials,
240
+ textures,
241
+ uniforms
242
+ });
243
+ const scene = new Scene();
244
+ scene.add(pass);
245
+ return {
246
+ renderer,
247
+ scene,
248
+ camera: new Camera(),
249
+ pass,
250
+ materials,
251
+ textures,
252
+ uniforms,
253
+ lensCount: setup.lenses.length,
254
+ seamProof
255
+ };
256
+ }
257
+ /**
258
+ * Compiles and proves every picture program, then opens the seam meter, which proves its own
259
+ * (gain matching asks for one at every load); what was built goes if a program fails.
260
+ */
261
+ function provenSeamMeter(renderer, built) {
262
+ const { pass, materials, textures, uniforms } = built;
263
+ try {
264
+ compilePictureMaterials(renderer, pass.geometry, materials);
265
+ return new SeamMeterPass(renderer, uniforms, uniforms.uLensCount.value);
266
+ } catch (error) {
267
+ disposePictureMaterials(materials);
268
+ pass.geometry.dispose();
269
+ for (const texture of textures) texture.dispose();
270
+ throw error;
271
+ }
272
+ }
273
+ //#endregion
274
+ export { ThreeFrameRenderer };
@@ -0,0 +1,14 @@
1
+ //#region ../../packages/adapters/three/src/compileAndProve.ts
2
+ /**
3
+ * Compiles the programs `scene` needs and asks every program the renderer holds for its uniforms.
4
+ * Three checks a program's link at its first use, and that request is one, so a shader the GPU
5
+ * refuses fails here through the renderer's shader-error hook without a draw (which costs a
6
+ * software renderer hundreds of milliseconds on the main thread).
7
+ */
8
+ function compileAndProve(renderer, scene, camera) {
9
+ renderer.compile(scene, camera);
10
+ const programs = renderer.info.programs ?? [];
11
+ for (const program of programs) program.getUniforms();
12
+ }
13
+ //#endregion
14
+ export { compileAndProve };
@@ -0,0 +1,52 @@
1
+ import { SHADER_DEFINES } from "./rendererUniforms.js";
2
+ import fullscreen_vert_default from "./shaders/fullscreen.vert.js";
3
+ import { BufferGeometry, Float32BufferAttribute, GLSL3, RawShaderMaterial } from "three";
4
+ //#region ../../packages/adapters/three/src/fullscreenPass.ts
5
+ /**
6
+ * One triangle covering the clip square: two corners lie beyond it so its hypotenuse clears the
7
+ * far edge. The fragment shader decides every pixel.
8
+ */
9
+ var BEYOND_CLIP = 3;
10
+ var FULLSCREEN_TRIANGLE = [
11
+ -1,
12
+ -1,
13
+ 0,
14
+ BEYOND_CLIP,
15
+ -1,
16
+ 0,
17
+ -1,
18
+ BEYOND_CLIP,
19
+ 0
20
+ ];
21
+ var POSITION_COMPONENTS = 3;
22
+ function createFullscreenTriangle() {
23
+ const geometry = new BufferGeometry();
24
+ geometry.setAttribute("position", new Float32BufferAttribute(FULLSCREEN_TRIANGLE, POSITION_COMPONENTS));
25
+ return geometry;
26
+ }
27
+ /**
28
+ * The program of a pass whose chunks refer only to the constants of `rendererUniforms`.
29
+ */
30
+ function programOf(chunks) {
31
+ return {
32
+ chunks,
33
+ defines: SHADER_DEFINES
34
+ };
35
+ }
36
+ /**
37
+ * A fullscreen pass over the shared uniforms. The raw shaders ignore the camera three still
38
+ * wants to render with; each pass keeps its own.
39
+ */
40
+ function createPassMaterial(uniforms, program) {
41
+ return new RawShaderMaterial({
42
+ glslVersion: GLSL3,
43
+ defines: { ...program.defines },
44
+ vertexShader: fullscreen_vert_default,
45
+ fragmentShader: program.chunks.join("\n"),
46
+ uniforms: { ...uniforms },
47
+ depthTest: false,
48
+ depthWrite: false
49
+ });
50
+ }
51
+ //#endregion
52
+ export { createFullscreenTriangle, createPassMaterial, programOf };
@@ -0,0 +1,2 @@
1
+ import { ThreeFrameRenderer } from "./ThreeFrameRenderer.js";
2
+ export { ThreeFrameRenderer };
@@ -0,0 +1,25 @@
1
+ import { LinearFilter, NoColorSpace, VideoFrameTexture } from "three";
2
+ //#region ../../packages/adapters/three/src/lensTextures.ts
3
+ /**
4
+ * The textures the decoded frames are uploaded to, one per frame of a pair, read with the
5
+ * given strategy. Three uploads a video frame with `texImage2D` and builds the mip chain after
6
+ * it when asked; the anisotropy applies where the driver offers it and the filter uses the
7
+ * chain.
8
+ */
9
+ function createLensTextures(count, strategy) {
10
+ return Array.from({ length: count }, () => {
11
+ const texture = new VideoFrameTexture();
12
+ texture.flipY = false;
13
+ texture.colorSpace = NoColorSpace;
14
+ texture.magFilter = LinearFilter;
15
+ applyTextureFilters(texture, strategy);
16
+ return texture;
17
+ });
18
+ }
19
+ function applyTextureFilters(texture, strategy) {
20
+ texture.minFilter = strategy.minFilter;
21
+ texture.generateMipmaps = strategy.generateMipmaps;
22
+ texture.anisotropy = strategy.anisotropy;
23
+ }
24
+ //#endregion
25
+ export { applyTextureFilters, createLensTextures };
@@ -0,0 +1,44 @@
1
+ import { SHADER_DEFINES } from "./rendererUniforms.js";
2
+ import { createPassMaterial } from "./fullscreenPass.js";
3
+ import { compileAndProve } from "./compileAndProve.js";
4
+ import { PICTURE_PROGRAMS } from "./shaderPrograms.js";
5
+ import { Camera, Mesh, Scene } from "three";
6
+ //#region ../../packages/adapters/three/src/pictureMaterials.ts
7
+ /**
8
+ * The pictures the player draws: the stitch through the fixed seam join.
9
+ */
10
+ var PLAYER_SHADERS = {
11
+ programs: PICTURE_PROGRAMS,
12
+ defines: SHADER_DEFINES
13
+ };
14
+ /**
15
+ * The pictures' materials over the shared uniforms, and any the pictures' programs add.
16
+ */
17
+ function createPictureMaterials(uniforms, shaders) {
18
+ const materialOf = (kind) => createPassMaterial(uniforms, {
19
+ chunks: shaders.programs[kind],
20
+ defines: shaders.defines
21
+ });
22
+ return {
23
+ rectilinear: materialOf("rectilinear"),
24
+ equirectangular: materialOf("equirectangular"),
25
+ "lens-tiles": materialOf("lens-tiles")
26
+ };
27
+ }
28
+ /**
29
+ * Compiles and proves every program now: a broken shader then fails here rather than at the first
30
+ * presented frame or the first change of view mode, and a change of mode is instant.
31
+ */
32
+ function compilePictureMaterials(renderer, geometry, materials) {
33
+ const camera = new Camera();
34
+ for (const material of Object.values(materials)) {
35
+ const scene = new Scene();
36
+ scene.add(new Mesh(geometry, material));
37
+ compileAndProve(renderer, scene, camera);
38
+ }
39
+ }
40
+ function disposePictureMaterials(materials) {
41
+ for (const material of Object.values(materials)) material.dispose();
42
+ }
43
+ //#endregion
44
+ export { PLAYER_SHADERS, compilePictureMaterials, createPictureMaterials, disposePictureMaterials };
@@ -0,0 +1,15 @@
1
+ //#region ../../packages/adapters/three/src/renderInto.ts
2
+ /**
3
+ * Runs `draw` with `target` bound, the renderer's own target restored however the drawing ends.
4
+ */
5
+ function renderInto(renderer, target, draw) {
6
+ const previous = renderer.getRenderTarget();
7
+ renderer.setRenderTarget(target);
8
+ try {
9
+ draw();
10
+ } finally {
11
+ renderer.setRenderTarget(previous);
12
+ }
13
+ }
14
+ //#endregion
15
+ export { renderInto };
@@ -0,0 +1,157 @@
1
+ import { ensureIndexInRange, ensureInvariant } from "../../../core/src/shared/errors/GyroViewError.js";
2
+ import { IDENTITY_MATRIX3 } from "../../../core/src/shared/math/Matrix3.js";
3
+ import { aspectOfArea, planeHalfExtentOf } from "../../../core/src/domain/view/rectilinear.js";
4
+ import "../../../core/src/index.js";
5
+ import "./samplingStrategies.js";
6
+ import { Matrix3, Vector2, Vector3, Vector4 } from "three";
7
+ var LENS_MEI = 0;
8
+ var LENS_RADIAL_POLYNOMIAL = 1;
9
+ /**
10
+ * The constants the player's GLSL sources refer to, injected as preprocessor defines so that
11
+ * this file is their only home; the lab adds its own.
12
+ */
13
+ var SHADER_DEFINES = Object.fromEntries([
14
+ ["MAX_LENSES", 2],
15
+ ["LENS_MEI", LENS_MEI],
16
+ ["LENS_RADIAL_POLYNOMIAL", LENS_RADIAL_POLYNOMIAL],
17
+ ["SAMPLING_BILINEAR", 0],
18
+ ["SAMPLING_TRILINEAR", 1],
19
+ ["SAMPLING_SUPERSAMPLED", 2]
20
+ ]);
21
+ function createRendererUniforms(setup, textures) {
22
+ const lenses = padded(setup.lenses, "a stitching setup has one or two lenses");
23
+ return {
24
+ ...viewUniforms(setup),
25
+ ...projectionUniforms(lenses),
26
+ ...samplingUniforms(lenses),
27
+ uTexture0: { value: textures[0] ?? null },
28
+ uTexture1: { value: textures[1] ?? null }
29
+ };
30
+ }
31
+ function viewUniforms(setup) {
32
+ return {
33
+ uLensCount: { value: setup.lenses.length },
34
+ uViewRotation: { value: new Matrix3() },
35
+ uStabilization: { value: new Matrix3() },
36
+ uPlaneHalfExtent: { value: 1 },
37
+ uPictureAspect: { value: 1 },
38
+ uScreenArea: { value: Array.from({ length: 2 }, () => new Vector4(0, 0, 1, 1)) },
39
+ uFeather: { value: new Vector2(setup.feather.start, setup.feather.end) }
40
+ };
41
+ }
42
+ function projectionUniforms(lenses) {
43
+ const slots = lenses.map((lens) => slotOf(lens.projection));
44
+ return {
45
+ uLensRotation: { value: lenses.map((lens) => toThreeMatrix(lens.rotation)) },
46
+ uLensKind: { value: slots.map((slot) => slot.kind) },
47
+ uLensPrincipalPoint: { value: lenses.map((lens) => toVector2(lens.projection.principalPoint)) },
48
+ uLensFocal: { value: slots.map((slot) => slot.focal) },
49
+ uLensXi: { value: slots.map((slot) => slot.xi) },
50
+ uLensRadial: { value: slots.map((slot) => slot.radial) },
51
+ uLensTangential: { value: slots.map((slot) => slot.tangential) },
52
+ uLensPolynomial: { value: slots.map((slot) => slot.polynomial) },
53
+ uLensHalfFov: { value: lenses.map((lens) => lens.halfFieldOfView) }
54
+ };
55
+ }
56
+ /**
57
+ * Exhaustive over the projection kinds: a new kind does not compile until it is packed.
58
+ */
59
+ function slotOf(projection) {
60
+ switch (projection.kind) {
61
+ case "mei": return {
62
+ kind: LENS_MEI,
63
+ focal: new Vector2(...projection.focal),
64
+ xi: projection.xi,
65
+ radial: new Vector3(...projection.radial),
66
+ tangential: new Vector2(...projection.tangential),
67
+ polynomial: new Vector4()
68
+ };
69
+ case "radial-polynomial": return {
70
+ kind: LENS_RADIAL_POLYNOMIAL,
71
+ focal: new Vector2(),
72
+ xi: 0,
73
+ radial: new Vector3(),
74
+ tangential: new Vector2(),
75
+ polynomial: new Vector4(...projection.coefficients)
76
+ };
77
+ }
78
+ }
79
+ function samplingUniforms(lenses) {
80
+ return {
81
+ uLensWindow: { value: lenses.map((lens) => new Vector4(lens.window.x, lens.window.y, lens.window.width, lens.window.height)) },
82
+ uLensRegion: { value: lenses.map((lens) => new Vector4(lens.region.x, lens.region.y, lens.region.width, lens.region.height)) },
83
+ uLensTexture: { value: lenses.map((lens) => lens.frameSlot) },
84
+ uLensGain: { value: lenses.map(() => new Vector3(1, 1, 1)) },
85
+ uSampling: { value: 0 }
86
+ };
87
+ }
88
+ function applyShaderSampling(uniforms, strategy) {
89
+ uniforms.uSampling.value = strategy.shaderSampling;
90
+ }
91
+ function applyStabilization(uniforms, rotation) {
92
+ uniforms.uStabilization.value = toThreeMatrix(rotation);
93
+ }
94
+ /**
95
+ * Sets one lens's per-channel multiplier; a lens the setup does not have is a defect.
96
+ */
97
+ function applyLensGain(uniforms, lensIndex, gain) {
98
+ ensureIndexInRange(lensIndex, uniforms.uLensCount.value, "lens");
99
+ uniforms.uLensGain.value[lensIndex]?.set(...gain);
100
+ }
101
+ var NEUTRAL = {
102
+ rotation: IDENTITY_MATRIX3,
103
+ planeHalfExtent: 1,
104
+ pictureAspect: 1
105
+ };
106
+ /**
107
+ * Sets the uniforms `picture` is drawn from, on a viewport `viewportAspect` wide per unit of
108
+ * height.
109
+ */
110
+ function applyPicture(uniforms, picture, viewportAspect) {
111
+ const values = valuesOf(picture, viewportAspect);
112
+ uniforms.uViewRotation.value = toThreeMatrix(values.rotation);
113
+ uniforms.uPlaneHalfExtent.value = values.planeHalfExtent;
114
+ uniforms.uPictureAspect.value = values.pictureAspect;
115
+ uniforms.uScreenArea.value = padded(values.areas, "a picture fills one area per lens at most").map((area) => toVector4(area));
116
+ }
117
+ function valuesOf(picture, viewportAspect) {
118
+ switch (picture.kind) {
119
+ case "rectilinear": return {
120
+ rotation: picture.rotation,
121
+ planeHalfExtent: planeHalfExtentOf(picture.fieldOfView),
122
+ pictureAspect: aspectOfArea(picture.area, viewportAspect),
123
+ areas: [picture.area]
124
+ };
125
+ case "equirectangular": return {
126
+ ...NEUTRAL,
127
+ rotation: picture.rotation,
128
+ areas: [picture.area]
129
+ };
130
+ case "lens-tiles": return {
131
+ ...NEUTRAL,
132
+ areas: picture.tiles
133
+ };
134
+ }
135
+ }
136
+ function toVector4(area) {
137
+ return new Vector4(area.x, area.y, area.width, area.height);
138
+ }
139
+ /**
140
+ * Row-major core matrix into a three matrix, whose `set` takes rows too.
141
+ */
142
+ function toThreeMatrix(m) {
143
+ return new Matrix3().set(...m);
144
+ }
145
+ function toVector2(point) {
146
+ return new Vector2(point.x, point.y);
147
+ }
148
+ /**
149
+ * Uniform arrays have a fixed length; unused slots repeat the last entry and are never read.
150
+ */
151
+ function padded(entries, expectation) {
152
+ const last = entries.at(-1);
153
+ ensureInvariant(last !== void 0 && entries.length <= 2, expectation);
154
+ return Array.from({ length: 2 }, (_unused, index) => entries[index] ?? last);
155
+ }
156
+ //#endregion
157
+ export { SHADER_DEFINES, applyLensGain, applyPicture, applyShaderSampling, applyStabilization, createRendererUniforms };
@@ -0,0 +1,26 @@
1
+ import { LinearFilter, LinearMipmapLinearFilter } from "three";
2
+ /**
3
+ * The record keyed by the quality refuses to compile until every tier has a strategy.
4
+ */
5
+ var SAMPLING_STRATEGIES = {
6
+ fast: {
7
+ minFilter: LinearFilter,
8
+ generateMipmaps: false,
9
+ anisotropy: 1,
10
+ shaderSampling: 0
11
+ },
12
+ balanced: {
13
+ minFilter: LinearMipmapLinearFilter,
14
+ generateMipmaps: true,
15
+ anisotropy: 4,
16
+ shaderSampling: 1
17
+ },
18
+ high: {
19
+ minFilter: LinearMipmapLinearFilter,
20
+ generateMipmaps: true,
21
+ anisotropy: 8,
22
+ shaderSampling: 2
23
+ }
24
+ };
25
+ //#endregion
26
+ export { SAMPLING_STRATEGIES };
@@ -0,0 +1,79 @@
1
+ import "../rendererUniforms.js";
2
+ import { createFullscreenTriangle, createPassMaterial, programOf } from "../fullscreenPass.js";
3
+ import { rowMeansOf } from "./rowMeans.js";
4
+ import { compileAndProve } from "../compileAndProve.js";
5
+ import { renderInto } from "../renderInto.js";
6
+ import { SEAM_ANALYSIS } from "../shaderPrograms.js";
7
+ import { Camera, Mesh, Scene, WebGLRenderTarget } from "three";
8
+ //#region ../../packages/adapters/three/src/seamMeter/SeamMeterPass.ts
9
+ /**
10
+ * Samples taken around the seam ring per lens: enough to average out content, few enough that
11
+ * the read-back is negligible.
12
+ */
13
+ var SEAM_SAMPLES = 64;
14
+ /**
15
+ * SeamMeter over the GPU: renders what each lens sees along the seam ring into one row of a tiny
16
+ * target and reads the rows back, sharing the stitch's uniforms so it always looks at the frames
17
+ * on screen. Compiled and proven when created, so a broken shader fails there and not
18
+ * mid-playback.
19
+ */
20
+ var SeamMeterPass = class {
21
+ renderer;
22
+ lensCount;
23
+ target = new WebGLRenderTarget(SEAM_SAMPLES, 2, {
24
+ depthBuffer: false,
25
+ stencilBuffer: false
26
+ });
27
+ pixels = new Uint8Array(SEAM_SAMPLES * 2 * 4);
28
+ camera = new Camera();
29
+ scene = new Scene();
30
+ material;
31
+ geometry = createFullscreenTriangle();
32
+ isDisposed = false;
33
+ constructor(renderer, uniforms, lensCount) {
34
+ this.renderer = renderer;
35
+ this.lensCount = lensCount;
36
+ this.material = createPassMaterial(uniforms, programOf(SEAM_ANALYSIS));
37
+ this.scene.add(new Mesh(this.geometry, this.material));
38
+ try {
39
+ compileAndProve(renderer, this.scene, this.camera);
40
+ } catch (error) {
41
+ this.dispose();
42
+ throw error;
43
+ }
44
+ }
45
+ /**
46
+ * Mean colour (0..1) each lens shows along the seam, or undefined when a lens images none of it.
47
+ */
48
+ async measure() {
49
+ try {
50
+ await this.readSeam();
51
+ } catch (error) {
52
+ if (this.isDisposed || this.renderer.getContext().isContextLost()) return void 0;
53
+ throw error;
54
+ }
55
+ return this.isDisposed ? void 0 : rowMeansOf(this.pixels, SEAM_SAMPLES, this.lensCount);
56
+ }
57
+ dispose() {
58
+ if (this.isDisposed) return;
59
+ this.isDisposed = true;
60
+ this.target.dispose();
61
+ this.material.dispose();
62
+ this.geometry.dispose();
63
+ }
64
+ /**
65
+ * Renders the seam rows into the meter's target, the renderer's own target restored however the
66
+ * drawing ends.
67
+ */
68
+ draw() {
69
+ renderInto(this.renderer, this.target, () => {
70
+ this.renderer.render(this.scene, this.camera);
71
+ });
72
+ }
73
+ async readSeam() {
74
+ this.draw();
75
+ await this.renderer.readRenderTargetPixelsAsync(this.target, 0, 0, SEAM_SAMPLES, 2, this.pixels);
76
+ }
77
+ };
78
+ //#endregion
79
+ export { SeamMeterPass };