@realsee/equirect-toolbox 1.0.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 (40) hide show
  1. package/README.md +184 -0
  2. package/configs/schema/cube.schema.json +40 -0
  3. package/configs/schema/rectlinear.schema.json +105 -0
  4. package/configs/templates/cube.json +8 -0
  5. package/configs/templates/rectlinear-euler.json +12 -0
  6. package/configs/templates/rectlinear-matrix.json +10 -0
  7. package/configs/templates/rectlinear-quaternion.json +15 -0
  8. package/dist/cli.d.ts +5 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +116 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/cube2equirect.d.ts +7 -0
  13. package/dist/cube2equirect.d.ts.map +1 -0
  14. package/dist/cube2equirect.js +77 -0
  15. package/dist/cube2equirect.js.map +1 -0
  16. package/dist/equirect2cube.d.ts +7 -0
  17. package/dist/equirect2cube.d.ts.map +1 -0
  18. package/dist/equirect2cube.js +63 -0
  19. package/dist/equirect2cube.js.map +1 -0
  20. package/dist/lib/create-webgl-canvas.d.ts +58 -0
  21. package/dist/lib/create-webgl-canvas.d.ts.map +1 -0
  22. package/dist/lib/create-webgl-canvas.js +191 -0
  23. package/dist/lib/create-webgl-canvas.js.map +1 -0
  24. package/dist/lib/cube-manifest.d.ts +11 -0
  25. package/dist/lib/cube-manifest.d.ts.map +1 -0
  26. package/dist/lib/cube-manifest.js +94 -0
  27. package/dist/lib/cube-manifest.js.map +1 -0
  28. package/dist/lib/cube-output.d.ts +9 -0
  29. package/dist/lib/cube-output.d.ts.map +1 -0
  30. package/dist/lib/cube-output.js +23 -0
  31. package/dist/lib/cube-output.js.map +1 -0
  32. package/dist/lib/headless-three.d.ts +8 -0
  33. package/dist/lib/headless-three.d.ts.map +1 -0
  34. package/dist/lib/headless-three.js +24 -0
  35. package/dist/lib/headless-three.js.map +1 -0
  36. package/dist/rectlinear.d.ts +36 -0
  37. package/dist/rectlinear.d.ts.map +1 -0
  38. package/dist/rectlinear.js +128 -0
  39. package/dist/rectlinear.js.map +1 -0
  40. package/package.json +72 -0
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # @realsee/equirect-toolbox
2
+
3
+ Node.js command-line tools for working with equirectangular panoramas. The package can render perspective (rectlinear) screenshots and convert between equirectangular images and six-face cube maps.
4
+
5
+ ## Features
6
+
7
+ - Render a perspective screenshot from an equirectangular panorama with Euler angles, a rotation matrix, or a quaternion.
8
+ - Convert an equirectangular panorama to six cube-face images.
9
+ - Convert six cube-face images described by a `cube.json` manifest back to an equirectangular panorama.
10
+ - Run from npm without a browser or a separate Three.js application.
11
+
12
+ ## Requirements
13
+
14
+ - Node.js 20 or 22. The supported range is declared as `^20 || ^22` in `package.json`.
15
+ - A platform supported by the native dependencies `gl` and `@napi-rs/canvas`.
16
+
17
+ ## Installation
18
+
19
+ Install the package in a project:
20
+
21
+ ```bash
22
+ npm install @realsee/equirect-toolbox
23
+ ```
24
+
25
+ The package provides the `equirect-toolbox` executable. It can be invoked with `npx`:
26
+
27
+ ```bash
28
+ npx equirect-toolbox --help
29
+ ```
30
+
31
+ Or invoke the binary from an npm script or from `node_modules/.bin` after installation.
32
+
33
+ ## CLI usage
34
+
35
+ ### `rectlinear`
36
+
37
+ Render a perspective screenshot from an equirectangular image:
38
+
39
+ ```bash
40
+ equirect-toolbox rectlinear \
41
+ --input equirect.jpg \
42
+ --output perspective.jpg \
43
+ --config rectlinear-matrix.json
44
+ ```
45
+
46
+ `--quality` controls JPEG output quality and defaults to `95`:
47
+
48
+ ```bash
49
+ equirect-toolbox rectlinear \
50
+ -i equirect.jpg \
51
+ -o perspective.jpg \
52
+ -c rectlinear-matrix.json \
53
+ --quality 90
54
+ ```
55
+
56
+ The output format is selected from the output file extension (`.jpg`, `.jpeg`, or `.png`).
57
+
58
+ #### Generate a rectlinear config
59
+
60
+ Use one of the built-in configuration templates as a starting point:
61
+
62
+ ```bash
63
+ equirect-toolbox rectlinear --init-config euler --output rectlinear-euler.json
64
+ equirect-toolbox rectlinear --init-config matrix --output rectlinear-matrix.json
65
+ equirect-toolbox rectlinear --init-config quaternion --output rectlinear-quaternion.json
66
+ ```
67
+
68
+ The generated file can be edited and passed with `--config`.
69
+
70
+ #### Rectlinear config format
71
+
72
+ All rectlinear configs contain a viewport and a camera field:
73
+
74
+ ```json
75
+ {
76
+ "viewport": {
77
+ "width": 1920,
78
+ "height": 1080
79
+ },
80
+ "camera": {
81
+ "vfov": 60,
82
+ "yaw": 0,
83
+ "pitch": 0,
84
+ "roll": 0
85
+ }
86
+ }
87
+ ```
88
+
89
+ - `viewport.width` and `viewport.height` are the output dimensions in pixels.
90
+ - `camera.vfov` is the vertical field of view in degrees.
91
+ - The camera orientation can be specified in one of three forms:
92
+ - Euler angles: `yaw`, `pitch`, and `roll`, in degrees.
93
+ - A row-major `3x3` rotation matrix.
94
+ - A quaternion with `x`, `y`, `z`, and `w` components.
95
+
96
+ The package includes the schema and templates in `configs/schema` and `configs/templates`. The generated templates are also available in the source repository.
97
+
98
+ ### `equirect2cube`
99
+
100
+ Convert an equirectangular panorama into six cube-face images and a manifest:
101
+
102
+ ```bash
103
+ equirect-toolbox equirect2cube \
104
+ --input equirect.jpg \
105
+ --output cube.json
106
+ ```
107
+
108
+ When `cube.json` does not exist, the command creates it and uses JPEG paths by default. The face size is calculated automatically from the input image and rounded up to a power of two.
109
+
110
+ When `cube.json` already exists, its six paths and file extensions determine where and how the face images are written. This means the command does not need separate `--format` or `--face-size` options.
111
+
112
+ To create an empty manifest without converting an image:
113
+
114
+ ```bash
115
+ equirect-toolbox equirect2cube \
116
+ --init-cube-json \
117
+ --output cube.json
118
+ ```
119
+
120
+ JPEG quality defaults to `95`:
121
+
122
+ ```bash
123
+ equirect-toolbox equirect2cube \
124
+ -i equirect.jpg \
125
+ -o cube.json \
126
+ --quality 90
127
+ ```
128
+
129
+ ### `cube2equirect`
130
+
131
+ Convert the six cube faces referenced by a manifest into an equirectangular panorama:
132
+
133
+ ```bash
134
+ equirect-toolbox cube2equirect \
135
+ --input cube.json \
136
+ --output equirect.jpg
137
+ ```
138
+
139
+ The input must be a `cube.json` manifest. The six images must have the same square, power-of-two dimensions. The output dimensions are four times the face size by two times the face size.
140
+
141
+ You can also initialize a manifest with this command:
142
+
143
+ ```bash
144
+ equirect-toolbox cube2equirect \
145
+ --init-cube-json \
146
+ --output cube.json
147
+ ```
148
+
149
+ ## `cube.json` manifest
150
+
151
+ A manifest contains one path for each cube face:
152
+
153
+ ```json
154
+ {
155
+ "px": "cube_px.jpg",
156
+ "nx": "cube_nx.jpg",
157
+ "py": "cube_py.jpg",
158
+ "ny": "cube_ny.jpg",
159
+ "pz": "cube_pz.jpg",
160
+ "nz": "cube_nz.jpg"
161
+ }
162
+ ```
163
+
164
+ The paths are resolved relative to the directory containing `cube.json`, not relative to the current working directory.
165
+
166
+ | Key | Cube face |
167
+ | ---- | ---------- |
168
+ | `px` | positive X |
169
+ | `nx` | negative X |
170
+ | `py` | positive Y |
171
+ | `ny` | negative Y |
172
+ | `pz` | positive Z |
173
+ | `nz` | negative Z |
174
+
175
+ The file extension in each path selects the output image format. Use `.jpg`/`.jpeg` for JPEG or `.png` for PNG. The manifest schema is included at `configs/schema/cube.schema.json`.
176
+
177
+ ## Package contents
178
+
179
+ The published package contains:
180
+
181
+ - `dist`: compiled JavaScript, declarations, declaration maps, and source maps.
182
+ - `configs/schema`: JSON Schemas used to validate rectlinear configs and cube manifests.
183
+ - `configs/templates`: ready-to-copy configuration and manifest templates.
184
+ - `README.md` and package metadata.
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Cube face manifest",
4
+ "description": "The six image files that make up a cubemap. Paths are resolved relative to this JSON file.",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["px", "nx", "py", "ny", "pz", "nz"],
8
+ "properties": {
9
+ "px": {
10
+ "type": "string",
11
+ "minLength": 1,
12
+ "description": "Right (+X) face image path."
13
+ },
14
+ "nx": {
15
+ "type": "string",
16
+ "minLength": 1,
17
+ "description": "Left (-X) face image path."
18
+ },
19
+ "py": {
20
+ "type": "string",
21
+ "minLength": 1,
22
+ "description": "Top (+Y) face image path."
23
+ },
24
+ "ny": {
25
+ "type": "string",
26
+ "minLength": 1,
27
+ "description": "Bottom (-Y) face image path."
28
+ },
29
+ "pz": {
30
+ "type": "string",
31
+ "minLength": 1,
32
+ "description": "Front (+Z) face image path."
33
+ },
34
+ "nz": {
35
+ "type": "string",
36
+ "minLength": 1,
37
+ "description": "Back (-Z) face image path."
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,105 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Equirectangular rectlinear screenshot configuration",
4
+ "description": "Configuration for rendering a perspective (rectlinear) screenshot from an equirectangular panorama.",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["viewport", "camera"],
8
+ "properties": {
9
+ "viewport": {
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "required": ["width", "height"],
13
+ "properties": {
14
+ "width": {
15
+ "type": "integer",
16
+ "minimum": 1,
17
+ "description": "Output width in pixels."
18
+ },
19
+ "height": {
20
+ "type": "integer",
21
+ "minimum": 1,
22
+ "description": "Output height in pixels."
23
+ }
24
+ }
25
+ },
26
+ "camera": {
27
+ "type": "object",
28
+ "unevaluatedProperties": false,
29
+ "allOf": [
30
+ {
31
+ "title": "Camera intrinsics",
32
+ "type": "object",
33
+ "required": ["vfov"],
34
+ "properties": {
35
+ "vfov": {
36
+ "type": "number",
37
+ "exclusiveMinimum": 0,
38
+ "exclusiveMaximum": 180,
39
+ "description": "Vertical field of view in degrees."
40
+ }
41
+ }
42
+ },
43
+ {
44
+ "title": "Camera extrinsics",
45
+ "oneOf": [
46
+ {
47
+ "title": "Euler extrinsics",
48
+ "type": "object",
49
+ "required": ["yaw", "pitch", "roll"],
50
+ "properties": {
51
+ "yaw": {
52
+ "type": "number",
53
+ "description": "Euler yaw in degrees."
54
+ },
55
+ "pitch": {
56
+ "type": "number",
57
+ "description": "Euler pitch in degrees."
58
+ },
59
+ "roll": {
60
+ "type": "number",
61
+ "description": "Euler roll in degrees."
62
+ }
63
+ }
64
+ },
65
+ {
66
+ "title": "Matrix extrinsics",
67
+ "type": "object",
68
+ "required": ["matrix"],
69
+ "properties": {
70
+ "matrix": {
71
+ "type": "array",
72
+ "description": "Row-major 3x3 camera rotation matrix.",
73
+ "minItems": 9,
74
+ "maxItems": 9,
75
+ "items": {
76
+ "type": "number"
77
+ }
78
+ }
79
+ }
80
+ },
81
+ {
82
+ "title": "Quaternion extrinsics",
83
+ "type": "object",
84
+ "required": ["quaternion"],
85
+ "properties": {
86
+ "quaternion": {
87
+ "type": "object",
88
+ "description": "Camera rotation quaternion.",
89
+ "additionalProperties": false,
90
+ "required": ["x", "y", "z", "w"],
91
+ "properties": {
92
+ "x": { "type": "number" },
93
+ "y": { "type": "number" },
94
+ "z": { "type": "number" },
95
+ "w": { "type": "number" }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ]
101
+ }
102
+ ]
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "px": "cube_px.jpg",
3
+ "nx": "cube_nx.jpg",
4
+ "py": "cube_py.jpg",
5
+ "ny": "cube_ny.jpg",
6
+ "pz": "cube_pz.jpg",
7
+ "nz": "cube_nz.jpg"
8
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "viewport": {
3
+ "width": 1920,
4
+ "height": 1080
5
+ },
6
+ "camera": {
7
+ "vfov": 60,
8
+ "yaw": 0,
9
+ "roll": 0,
10
+ "pitch": 0
11
+ }
12
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "viewport": {
3
+ "width": 1920,
4
+ "height": 1080
5
+ },
6
+ "camera": {
7
+ "vfov": 60,
8
+ "matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1]
9
+ }
10
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "viewport": {
3
+ "width": 1920,
4
+ "height": 1080
5
+ },
6
+ "camera": {
7
+ "vfov": 60,
8
+ "quaternion": {
9
+ "x": 0,
10
+ "y": 0,
11
+ "z": 0,
12
+ "w": 0
13
+ }
14
+ }
15
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ export declare function createProgram(): Command;
4
+ export declare function main(argv?: string[]): Promise<void>;
5
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,wBAAgB,aAAa,IAAI,OAAO,CAuGvC;AAED,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE"}
package/dist/cli.js ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { resolve } from "path";
4
+ import { fileURLToPath } from "url";
5
+ export function createProgram() {
6
+ const program = new Command();
7
+ program.name("equirect-toolbox").description("Command-line tools for equirectangular panoramas");
8
+ const rectlinear = program
9
+ .command("rectlinear")
10
+ .description("Render a perspective screenshot from an equirectangular panorama")
11
+ .option("-i, --input <path>", "input equirectangular image")
12
+ .option("-o, --output <path>", "output image or generated config path")
13
+ .option("-c, --config <path>", "rectlinear JSON config path")
14
+ .option("--init-config <type>", "generate a config template: euler, matrix, or quaternion")
15
+ .option("--quality <quality>", "JPEG quality from 0 to 100", parseQuality, 95);
16
+ rectlinear.action(async (options) => {
17
+ if (options.initConfig !== undefined) {
18
+ if (!options.output) {
19
+ throw new Error("The --output option is required when using --init-config.");
20
+ }
21
+ const { initializeRectlinearConfig } = await import("./rectlinear.js");
22
+ initializeRectlinearConfig(options.initConfig, options.output);
23
+ return;
24
+ }
25
+ if (!options.input || !options.output || !options.config) {
26
+ throw new Error("The --input, --output, and --config options are required for rectlinear rendering.");
27
+ }
28
+ const { renderRectlinear } = await import("./rectlinear.js");
29
+ await renderRectlinear({
30
+ input: options.input,
31
+ output: options.output,
32
+ config: options.config,
33
+ quality: options.quality,
34
+ });
35
+ });
36
+ const equirect2cube = program
37
+ .command("equirect2cube")
38
+ .description("Convert an equirectangular panorama to six cube face images")
39
+ .option("-i, --input <path>", "input equirectangular image")
40
+ .option("-o, --output <path>", "output cube.json manifest path")
41
+ .option("--init-cube-json", "generate a cube.json template")
42
+ .option("--quality <quality>", "JPEG quality from 0 to 100", parseQuality, 95);
43
+ equirect2cube.action(async (options) => {
44
+ if (options.initCubeJson) {
45
+ if (!options.output) {
46
+ throw new Error("The --output option is required when using --init-cube-json.");
47
+ }
48
+ const { initializeCubeJson } = await import("./lib/cube-manifest.js");
49
+ initializeCubeJson(options.output);
50
+ return;
51
+ }
52
+ if (!options.input || !options.output) {
53
+ throw new Error("The --input and --output options are required for equirect2cube rendering.");
54
+ }
55
+ const { convertEquirectToCube } = await import("./equirect2cube.js");
56
+ await convertEquirectToCube({
57
+ input: options.input,
58
+ output: options.output,
59
+ quality: options.quality,
60
+ });
61
+ });
62
+ const cube2equirect = program
63
+ .command("cube2equirect")
64
+ .description("Convert six cube face images described by cube.json to an equirectangular panorama")
65
+ .option("-i, --input <path>", "cube.json manifest path")
66
+ .option("-o, --output <path>", "output equirectangular image or generated cube.json path")
67
+ .option("--init-cube-json", "generate a cube.json template")
68
+ .option("--quality <quality>", "JPEG quality from 0 to 100", parseQuality, 95);
69
+ cube2equirect.action(async (options) => {
70
+ if (options.initCubeJson) {
71
+ if (!options.output) {
72
+ throw new Error("The --output option is required when using --init-cube-json.");
73
+ }
74
+ const { initializeCubeJson } = await import("./lib/cube-manifest.js");
75
+ initializeCubeJson(options.output);
76
+ return;
77
+ }
78
+ if (!options.input || !options.output) {
79
+ throw new Error("The --input cube.json and --output options are required for cube2equirect rendering.");
80
+ }
81
+ const { convertCubeToEquirect } = await import("./cube2equirect.js");
82
+ await convertCubeToEquirect({
83
+ input: options.input,
84
+ output: options.output,
85
+ quality: options.quality,
86
+ });
87
+ });
88
+ return program;
89
+ }
90
+ export async function main(argv = process.argv) {
91
+ await createProgram().parseAsync(normalizeArguments(argv));
92
+ }
93
+ function normalizeArguments(argv) {
94
+ return argv.map((argument) => {
95
+ if (argument === "-init-config")
96
+ return "--init-config";
97
+ if (argument.startsWith("-init-config="))
98
+ return `--init-config=${argument.slice("-init-config=".length)}`;
99
+ return argument;
100
+ });
101
+ }
102
+ function parseQuality(value) {
103
+ const quality = Number(value);
104
+ if (!Number.isInteger(quality) || quality < 0 || quality > 100) {
105
+ throw new Error("JPEG quality must be an integer from 0 to 100.");
106
+ }
107
+ return quality;
108
+ }
109
+ const entryPath = process.argv[1];
110
+ if (entryPath && resolve(entryPath) === resolve(fileURLToPath(import.meta.url))) {
111
+ main().catch((error) => {
112
+ console.error(error instanceof Error ? error.message : String(error));
113
+ process.exitCode = 1;
114
+ });
115
+ }
116
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type CubeToEquirectOptions = {
2
+ input: string;
3
+ output: string;
4
+ quality?: number;
5
+ };
6
+ export declare function convertCubeToEquirect(options: CubeToEquirectOptions): Promise<void>;
7
+ //# sourceMappingURL=cube2equirect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cube2equirect.d.ts","sourceRoot":"","sources":["../src/cube2equirect.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA6BF,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDzF"}
@@ -0,0 +1,77 @@
1
+ import * as THREE from "three";
2
+ import { loadImage } from "@napi-rs/canvas";
3
+ import { readCubeManifest } from "./lib/cube-manifest.js";
4
+ import { createHeadlessRenderer } from "./lib/headless-three.js";
5
+ import { getOutputFormat, isPowerOfTwo, writeCanvasImage, cubeFaceKeys, } from "./lib/cube-output.js";
6
+ const vertexShader = `
7
+ varying vec2 vUv;
8
+ void main() {
9
+ vUv = uv;
10
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
11
+ }
12
+ `;
13
+ const fragmentShader = `
14
+ varying vec2 vUv;
15
+ uniform samplerCube tDiffuse;
16
+
17
+ #define TAU 6.2831852
18
+ #define PI 3.1415926
19
+
20
+ void main() {
21
+ float theta = TAU * vUv.x;
22
+ float phi = PI * (vUv.y - 0.5);
23
+ float distance = abs(cos(phi));
24
+ float dx = -sin(theta) * distance;
25
+ float dy = sin(phi);
26
+ float dz = -cos(theta) * distance;
27
+ vec3 target = vec3(dx, dy, dz);
28
+ gl_FragColor = textureCube(tDiffuse, target);
29
+ }
30
+ `;
31
+ export async function convertCubeToEquirect(options) {
32
+ const manifest = readCubeManifest(options.input);
33
+ const images = await Promise.all(cubeFaceKeys.map((faceKey) => loadImage(manifest[faceKey])));
34
+ const size = images[0].width;
35
+ for (const image of images) {
36
+ if (!isPowerOfTwo(image.width) || !isPowerOfTwo(image.height)) {
37
+ throw new Error("Cube images must have power-of-two dimensions.");
38
+ }
39
+ if (image.width !== size || image.height !== size) {
40
+ throw new Error("Cube images must all have the same square dimensions.");
41
+ }
42
+ }
43
+ const outputWidth = size * 4;
44
+ const outputHeight = size * 2;
45
+ const format = getOutputFormat(options.output);
46
+ const quality = options.quality ?? 95;
47
+ const { canvas, renderer } = await createHeadlessRenderer(outputWidth, outputHeight);
48
+ let cubeTexture;
49
+ let material;
50
+ try {
51
+ cubeTexture = new THREE.CubeTexture(images);
52
+ cubeTexture.format = THREE.RGBAFormat;
53
+ cubeTexture.needsUpdate = true;
54
+ const camera = new THREE.OrthographicCamera(-1, 1, 0.5, -0.5, 1, 10);
55
+ camera.position.setZ(2);
56
+ const geometry = new THREE.PlaneGeometry(2, 1);
57
+ material = new THREE.ShaderMaterial({
58
+ uniforms: {
59
+ tDiffuse: { value: cubeTexture },
60
+ },
61
+ vertexShader,
62
+ fragmentShader,
63
+ });
64
+ const mesh = new THREE.Mesh(geometry, material);
65
+ const scene = new THREE.Scene();
66
+ scene.add(mesh);
67
+ renderer.setSize(outputWidth, outputHeight, false);
68
+ renderer.render(scene, camera);
69
+ writeCanvasImage(canvas, options.output, format, quality);
70
+ }
71
+ finally {
72
+ cubeTexture?.dispose();
73
+ material?.dispose();
74
+ renderer.dispose();
75
+ }
76
+ }
77
+ //# sourceMappingURL=cube2equirect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cube2equirect.js","sourceRoot":"","sources":["../src/cube2equirect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type EquirectToCubeOptions = {
2
+ input: string;
3
+ output: string;
4
+ quality?: number;
5
+ };
6
+ export declare function convertEquirectToCube(options: EquirectToCubeOptions): Promise<void>;
7
+ //# sourceMappingURL=equirect2cube.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"equirect2cube.d.ts","sourceRoot":"","sources":["../src/equirect2cube.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAWF,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDzF"}