@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.
- package/README.md +184 -0
- package/configs/schema/cube.schema.json +40 -0
- package/configs/schema/rectlinear.schema.json +105 -0
- package/configs/templates/cube.json +8 -0
- package/configs/templates/rectlinear-euler.json +12 -0
- package/configs/templates/rectlinear-matrix.json +10 -0
- package/configs/templates/rectlinear-quaternion.json +15 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/cube2equirect.d.ts +7 -0
- package/dist/cube2equirect.d.ts.map +1 -0
- package/dist/cube2equirect.js +77 -0
- package/dist/cube2equirect.js.map +1 -0
- package/dist/equirect2cube.d.ts +7 -0
- package/dist/equirect2cube.d.ts.map +1 -0
- package/dist/equirect2cube.js +63 -0
- package/dist/equirect2cube.js.map +1 -0
- package/dist/lib/create-webgl-canvas.d.ts +58 -0
- package/dist/lib/create-webgl-canvas.d.ts.map +1 -0
- package/dist/lib/create-webgl-canvas.js +191 -0
- package/dist/lib/create-webgl-canvas.js.map +1 -0
- package/dist/lib/cube-manifest.d.ts +11 -0
- package/dist/lib/cube-manifest.d.ts.map +1 -0
- package/dist/lib/cube-manifest.js +94 -0
- package/dist/lib/cube-manifest.js.map +1 -0
- package/dist/lib/cube-output.d.ts +9 -0
- package/dist/lib/cube-output.d.ts.map +1 -0
- package/dist/lib/cube-output.js +23 -0
- package/dist/lib/cube-output.js.map +1 -0
- package/dist/lib/headless-three.d.ts +8 -0
- package/dist/lib/headless-three.d.ts.map +1 -0
- package/dist/lib/headless-three.js +24 -0
- package/dist/lib/headless-three.js.map +1 -0
- package/dist/rectlinear.d.ts +36 -0
- package/dist/rectlinear.d.ts.map +1 -0
- package/dist/rectlinear.js +128 -0
- package/dist/rectlinear.js.map +1 -0
- 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
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -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
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 @@
|
|
|
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"}
|