poppygl 0.0.22 → 0.0.24
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/dist/index.d.ts +10 -1
- package/dist/index.js +193 -26
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -195,8 +195,17 @@ declare function drawInfiniteGrid(software_renderer: SoftwareRenderer, params: {
|
|
|
195
195
|
|
|
196
196
|
declare const pureImageFactory: ImageFactory;
|
|
197
197
|
|
|
198
|
+
declare function encodePNG(image: BitmapLike): Promise<Uint8Array>;
|
|
199
|
+
|
|
198
200
|
declare function encodePNGToBuffer(image: BitmapLike): Promise<Buffer>;
|
|
199
201
|
|
|
202
|
+
interface RenderGLTFToPNGFromURLOptions extends RenderOptionsInput, LoadGLTFWithResourcesFromURLOptions {
|
|
203
|
+
}
|
|
204
|
+
declare function renderGLTFToPNGFromURL(url: string, options?: RenderGLTFToPNGFromURLOptions): Promise<Uint8Array>;
|
|
205
|
+
|
|
206
|
+
type RenderGLTFToPNGFromGLBOptions = RenderOptionsInput;
|
|
207
|
+
declare function renderGLTFToPNGFromGLB(glb: ArrayBuffer | Uint8Array, options?: RenderGLTFToPNGFromGLBOptions): Promise<Uint8Array>;
|
|
208
|
+
|
|
200
209
|
interface RenderGLTFToPNGBufferFromURLOptions extends RenderOptionsInput, LoadGLTFWithResourcesFromURLOptions {
|
|
201
210
|
}
|
|
202
211
|
declare function renderGLTFToPNGBufferFromURL(url: string, options?: RenderGLTFToPNGBufferFromURLOptions): Promise<Buffer>;
|
|
@@ -210,4 +219,4 @@ declare function renderGLTFToPNGBuffer(gltfOrJson: string | any, options?: Rende
|
|
|
210
219
|
|
|
211
220
|
declare function renderSceneFromGLTF(scene: GLTFScene, options?: RenderOptionsInput, imageFactory?: ImageFactory): RenderResult;
|
|
212
221
|
|
|
213
|
-
export { type BitmapLike, type Camera, type CameraRotation, type CameraUp, DEFAULT_LIGHT_DIR, DEFAULT_RENDER_OPTIONS, type DebugPoint, type DrawCall, type FetchLike, type GLTFResources, type GLTFScene, type GridOptions, type ImageFactory, type LightSettings, type LoadGLTFWithResourcesFromURLOptions, type Material, type MutableRGBA, type RGBA, type RenderGLTFToPNGBufferFromGLBBufferOptions, type RenderGLTFToPNGBufferFromURLOptions, type RenderGLTFToPNGBufferOptions, type RenderOptions, type RenderOptionsInput, type RenderResult, SoftwareRenderer, bufferFromDataURI, buildCamera, computeSmoothNormals, computeWorldAABB, createGrid, createInfiniteGrid, createSceneFromGLTF, createUint8Bitmap, decodeImageFromBuffer, drawInfiniteGrid, encodePNGToBuffer, getDefaultRenderOptions, hexToRgb, isJPG, isPNG, loadGLTFWithResourcesFromURL, pureImageFactory, renderDrawCalls, renderGLTFToPNGBuffer, renderGLTFToPNGBufferFromGLBBuffer, renderGLTFToPNGBufferFromURL, renderSceneFromGLTF, resolveRenderOptions };
|
|
222
|
+
export { type BitmapLike, type Camera, type CameraRotation, type CameraUp, DEFAULT_LIGHT_DIR, DEFAULT_RENDER_OPTIONS, type DebugPoint, type DrawCall, type FetchLike, type GLTFResources, type GLTFScene, type GridOptions, type ImageFactory, type LightSettings, type LoadGLTFWithResourcesFromURLOptions, type Material, type MutableRGBA, type RGBA, type RenderGLTFToPNGBufferFromGLBBufferOptions, type RenderGLTFToPNGBufferFromURLOptions, type RenderGLTFToPNGBufferOptions, type RenderGLTFToPNGFromGLBOptions, type RenderGLTFToPNGFromURLOptions, type RenderOptions, type RenderOptionsInput, type RenderResult, SoftwareRenderer, bufferFromDataURI, buildCamera, computeSmoothNormals, computeWorldAABB, createGrid, createInfiniteGrid, createSceneFromGLTF, createUint8Bitmap, decodeImageFromBuffer, drawInfiniteGrid, encodePNG, encodePNGToBuffer, getDefaultRenderOptions, hexToRgb, isJPG, isPNG, loadGLTFWithResourcesFromURL, pureImageFactory, renderDrawCalls, renderGLTFToPNGBuffer, renderGLTFToPNGBufferFromGLBBuffer, renderGLTFToPNGBufferFromURL, renderGLTFToPNGFromGLB, renderGLTFToPNGFromURL, renderSceneFromGLTF, resolveRenderOptions };
|
package/dist/index.js
CHANGED
|
@@ -1610,12 +1610,51 @@ function createSceneFromGLTF(gltf, resources) {
|
|
|
1610
1610
|
}
|
|
1611
1611
|
|
|
1612
1612
|
// lib/gltf/resourceUtils.ts
|
|
1613
|
-
import {
|
|
1614
|
-
|
|
1613
|
+
import { decode } from "fast-png";
|
|
1614
|
+
|
|
1615
|
+
// lib/utils/bytes.ts
|
|
1616
|
+
function getNodeBuffer() {
|
|
1617
|
+
const maybeBuffer = globalThis.Buffer;
|
|
1618
|
+
if (!maybeBuffer) {
|
|
1619
|
+
throw new Error("Node Buffer API is not available in this runtime.");
|
|
1620
|
+
}
|
|
1621
|
+
return maybeBuffer;
|
|
1622
|
+
}
|
|
1623
|
+
function toUint8Array(chunk) {
|
|
1624
|
+
if (chunk instanceof Uint8Array) return chunk;
|
|
1625
|
+
if (chunk instanceof ArrayBuffer) return new Uint8Array(chunk);
|
|
1626
|
+
return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
1627
|
+
}
|
|
1628
|
+
function concatUint8Arrays(chunks) {
|
|
1629
|
+
const totalLength = chunks.reduce((length, chunk) => length + chunk.length, 0);
|
|
1630
|
+
const result = new Uint8Array(totalLength);
|
|
1631
|
+
let offset = 0;
|
|
1632
|
+
for (const chunk of chunks) {
|
|
1633
|
+
result.set(chunk, offset);
|
|
1634
|
+
offset += chunk.length;
|
|
1635
|
+
}
|
|
1636
|
+
return result;
|
|
1637
|
+
}
|
|
1638
|
+
function base64ToUint8Array(base64) {
|
|
1639
|
+
if (typeof globalThis.atob === "function") {
|
|
1640
|
+
const binary = globalThis.atob(base64);
|
|
1641
|
+
const bytes = new Uint8Array(binary.length);
|
|
1642
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1643
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1644
|
+
}
|
|
1645
|
+
return bytes;
|
|
1646
|
+
}
|
|
1647
|
+
return new Uint8Array(getNodeBuffer().from(base64, "base64"));
|
|
1648
|
+
}
|
|
1649
|
+
function uint8ArrayToNodeBuffer(bytes) {
|
|
1650
|
+
return getNodeBuffer().from(bytes);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// lib/gltf/resourceUtils.ts
|
|
1615
1654
|
function bufferFromDataURI(uri) {
|
|
1616
1655
|
const match = uri.match(/^data:.*?;base64,(.*)$/);
|
|
1617
1656
|
if (!match) throw new Error(`Unsupported data URI: ${uri.slice(0, 64)}...`);
|
|
1618
|
-
return
|
|
1657
|
+
return base64ToUint8Array(match[1]);
|
|
1619
1658
|
}
|
|
1620
1659
|
function isPNG(filenameOrUri) {
|
|
1621
1660
|
return /\.png(\?|$)/i.test(filenameOrUri) || /image\/png/i.test(filenameOrUri);
|
|
@@ -1630,20 +1669,35 @@ function detectMimeTypeFromBuffer(buf, hint) {
|
|
|
1630
1669
|
if (buf.length >= 2 && buf[0] === 255 && buf[1] === 216) return "image/jpeg";
|
|
1631
1670
|
return null;
|
|
1632
1671
|
}
|
|
1633
|
-
function
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1672
|
+
async function decodeImageViaFastPng(buf) {
|
|
1673
|
+
const decoded = decode(buf);
|
|
1674
|
+
return {
|
|
1675
|
+
width: decoded.width,
|
|
1676
|
+
height: decoded.height,
|
|
1677
|
+
data: new Uint8ClampedArray(decoded.data)
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
async function decodeImageViaPureImage(buf, mimeType) {
|
|
1681
|
+
const { PassThrough: PassThrough2 } = await import("readable-stream");
|
|
1682
|
+
const PImage3 = await import("pureimage");
|
|
1683
|
+
const stream = new PassThrough2();
|
|
1684
|
+
stream.end(buf);
|
|
1685
|
+
if (mimeType === "image/png") return PImage3.decodePNGFromStream(stream);
|
|
1686
|
+
if (mimeType === "image/jpeg" || mimeType === "image/jpg")
|
|
1687
|
+
return PImage3.decodeJPEGFromStream(stream);
|
|
1688
|
+
throw new Error(`Unsupported embedded image mimeType: ${mimeType}`);
|
|
1637
1689
|
}
|
|
1638
1690
|
async function decodeImageFromBuffer(buf, mimeType) {
|
|
1639
1691
|
const type = detectMimeTypeFromBuffer(buf, mimeType);
|
|
1640
|
-
if (type
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1692
|
+
if (!type) {
|
|
1693
|
+
throw new Error(
|
|
1694
|
+
`Unsupported embedded image mimeType: ${mimeType ?? "unknown"}`
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
if (type === "image/png") {
|
|
1698
|
+
return decodeImageViaFastPng(buf);
|
|
1699
|
+
}
|
|
1700
|
+
return decodeImageViaPureImage(buf, type);
|
|
1647
1701
|
}
|
|
1648
1702
|
|
|
1649
1703
|
// lib/gltf/parseGLB.ts
|
|
@@ -1799,26 +1853,136 @@ async function loadGLTFWithResourcesFromURL(url, options = {}) {
|
|
|
1799
1853
|
}
|
|
1800
1854
|
|
|
1801
1855
|
// lib/image/pureImageFactory.ts
|
|
1802
|
-
import * as
|
|
1803
|
-
var pureImageFactory = (width, height) =>
|
|
1856
|
+
import * as PImage from "pureimage";
|
|
1857
|
+
var pureImageFactory = (width, height) => PImage.make(width, height);
|
|
1858
|
+
|
|
1859
|
+
// lib/image/encodePNG.ts
|
|
1860
|
+
import { encode } from "fast-png";
|
|
1861
|
+
async function encodePNG(image) {
|
|
1862
|
+
return encode({
|
|
1863
|
+
width: image.width,
|
|
1864
|
+
height: image.height,
|
|
1865
|
+
data: image.data,
|
|
1866
|
+
channels: 4,
|
|
1867
|
+
depth: 8
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1804
1870
|
|
|
1805
1871
|
// lib/image/encodePNGToBuffer.ts
|
|
1806
|
-
import { PassThrough
|
|
1807
|
-
import * as
|
|
1872
|
+
import { PassThrough } from "readable-stream";
|
|
1873
|
+
import * as PImage2 from "pureimage";
|
|
1808
1874
|
async function encodePNGToBuffer(image) {
|
|
1809
|
-
const passThrough = new
|
|
1875
|
+
const passThrough = new PassThrough();
|
|
1810
1876
|
const chunks = [];
|
|
1811
|
-
passThrough.on(
|
|
1812
|
-
|
|
1813
|
-
|
|
1877
|
+
passThrough.on(
|
|
1878
|
+
"data",
|
|
1879
|
+
(chunk) => {
|
|
1880
|
+
chunks.push(toUint8Array(chunk));
|
|
1881
|
+
}
|
|
1882
|
+
);
|
|
1814
1883
|
const resultPromise = new Promise((resolve, reject) => {
|
|
1815
|
-
passThrough.on("end", () =>
|
|
1884
|
+
passThrough.on("end", () => {
|
|
1885
|
+
resolve(uint8ArrayToNodeBuffer(concatUint8Arrays(chunks)));
|
|
1886
|
+
});
|
|
1816
1887
|
passThrough.on("error", reject);
|
|
1817
1888
|
});
|
|
1818
|
-
await
|
|
1889
|
+
await PImage2.encodePNGToStream(image, passThrough);
|
|
1819
1890
|
return await resultPromise;
|
|
1820
1891
|
}
|
|
1821
1892
|
|
|
1893
|
+
// lib/render/renderGLTFToPNGFromURL.ts
|
|
1894
|
+
async function renderGLTFToPNGFromURL(url, options = {}) {
|
|
1895
|
+
const { fetchImpl, ...renderOptions } = options;
|
|
1896
|
+
const { gltf, resources } = await loadGLTFWithResourcesFromURL(url, {
|
|
1897
|
+
fetchImpl
|
|
1898
|
+
});
|
|
1899
|
+
const scene = createSceneFromGLTF(gltf, resources);
|
|
1900
|
+
const { bitmap } = renderDrawCalls(
|
|
1901
|
+
scene.drawCalls,
|
|
1902
|
+
renderOptions,
|
|
1903
|
+
createUint8Bitmap
|
|
1904
|
+
);
|
|
1905
|
+
return encodePNG(bitmap);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
// lib/render/renderGLTFToPNGFromGLB.ts
|
|
1909
|
+
function normalizeToArrayBuffer(source) {
|
|
1910
|
+
if (source instanceof ArrayBuffer) return source;
|
|
1911
|
+
const { buffer, byteOffset, byteLength } = source;
|
|
1912
|
+
const viewBuffer = buffer;
|
|
1913
|
+
return viewBuffer.slice(byteOffset, byteOffset + byteLength);
|
|
1914
|
+
}
|
|
1915
|
+
async function renderGLTFToPNGFromGLB(glb, options = {}) {
|
|
1916
|
+
const arrayBuffer = normalizeToArrayBuffer(glb);
|
|
1917
|
+
const { gltf, binaryChunk } = parseGLB(arrayBuffer);
|
|
1918
|
+
const totalBuffers = Array.isArray(gltf.buffers) ? gltf.buffers.length : 0;
|
|
1919
|
+
const buffers = new Array(totalBuffers);
|
|
1920
|
+
for (let index = 0; index < totalBuffers; index++) {
|
|
1921
|
+
const entry = gltf.buffers[index];
|
|
1922
|
+
if (entry?.uri) {
|
|
1923
|
+
if (!entry.uri.startsWith("data:")) {
|
|
1924
|
+
throw new Error(
|
|
1925
|
+
`Buffer ${index} references external resource ${entry.uri}. Use renderGLTFToPNGFromURL for GLBs with external buffer URIs.`
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1928
|
+
buffers[index] = bufferFromDataURI(entry.uri);
|
|
1929
|
+
continue;
|
|
1930
|
+
}
|
|
1931
|
+
if (!binaryChunk) {
|
|
1932
|
+
throw new Error(
|
|
1933
|
+
`GLB is missing the binary chunk required for buffer ${index}.`
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
buffers[index] = binaryChunk;
|
|
1937
|
+
}
|
|
1938
|
+
const resolveBufferViewSlice = (bufferViewIndex) => {
|
|
1939
|
+
const bufferView = gltf.bufferViews?.[bufferViewIndex];
|
|
1940
|
+
if (!bufferView) {
|
|
1941
|
+
throw new Error(`Invalid bufferView index ${bufferViewIndex}.`);
|
|
1942
|
+
}
|
|
1943
|
+
const bufferIndex = bufferView.buffer;
|
|
1944
|
+
const buffer = buffers[bufferIndex];
|
|
1945
|
+
if (!buffer) {
|
|
1946
|
+
throw new Error(
|
|
1947
|
+
`Missing buffer data for bufferView ${bufferViewIndex} (buffer ${bufferIndex}).`
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1950
|
+
const byteOffset = bufferView.byteOffset ?? 0;
|
|
1951
|
+
const byteLength = bufferView.byteLength;
|
|
1952
|
+
if (typeof byteLength !== "number") {
|
|
1953
|
+
throw new Error(`bufferView ${bufferViewIndex} is missing byteLength.`);
|
|
1954
|
+
}
|
|
1955
|
+
return buffer.subarray(byteOffset, byteOffset + byteLength);
|
|
1956
|
+
};
|
|
1957
|
+
const images = await Promise.all(
|
|
1958
|
+
(gltf.images ?? []).map(async (image, imageIndex) => {
|
|
1959
|
+
if (image.uri) {
|
|
1960
|
+
if (!image.uri.startsWith("data:")) {
|
|
1961
|
+
throw new Error(
|
|
1962
|
+
`Image ${imageIndex} references external resource ${image.uri}. Embed textures or load via renderGLTFToPNGFromURL.`
|
|
1963
|
+
);
|
|
1964
|
+
}
|
|
1965
|
+
const imageBytes = bufferFromDataURI(image.uri);
|
|
1966
|
+
return decodeImageFromBuffer(imageBytes, image.mimeType);
|
|
1967
|
+
}
|
|
1968
|
+
if (typeof image.bufferView === "number") {
|
|
1969
|
+
const slice = resolveBufferViewSlice(image.bufferView);
|
|
1970
|
+
return decodeImageFromBuffer(slice, image.mimeType);
|
|
1971
|
+
}
|
|
1972
|
+
throw new Error(
|
|
1973
|
+
`Image ${imageIndex} must supply a data URI or bufferView for GLB rendering.`
|
|
1974
|
+
);
|
|
1975
|
+
})
|
|
1976
|
+
);
|
|
1977
|
+
const scene = createSceneFromGLTF(gltf, { buffers, images });
|
|
1978
|
+
const { bitmap } = renderDrawCalls(
|
|
1979
|
+
scene.drawCalls,
|
|
1980
|
+
options,
|
|
1981
|
+
createUint8Bitmap
|
|
1982
|
+
);
|
|
1983
|
+
return encodePNG(bitmap);
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1822
1986
|
// lib/render/renderGLTFToPNGBufferFromURL.ts
|
|
1823
1987
|
async function renderGLTFToPNGBufferFromURL(url, options = {}) {
|
|
1824
1988
|
const { fetchImpl, ...renderOptions } = options;
|
|
@@ -1835,14 +1999,14 @@ async function renderGLTFToPNGBufferFromURL(url, options = {}) {
|
|
|
1835
1999
|
}
|
|
1836
2000
|
|
|
1837
2001
|
// lib/render/renderGLTFToPNGBufferFromGLBBuffer.ts
|
|
1838
|
-
function
|
|
2002
|
+
function normalizeToArrayBuffer2(source) {
|
|
1839
2003
|
if (source instanceof ArrayBuffer) return source;
|
|
1840
2004
|
const { buffer, byteOffset, byteLength } = source;
|
|
1841
2005
|
const viewBuffer = buffer;
|
|
1842
2006
|
return viewBuffer.slice(byteOffset, byteOffset + byteLength);
|
|
1843
2007
|
}
|
|
1844
2008
|
async function renderGLTFToPNGBufferFromGLBBuffer(glb, options = {}) {
|
|
1845
|
-
const arrayBuffer =
|
|
2009
|
+
const arrayBuffer = normalizeToArrayBuffer2(glb);
|
|
1846
2010
|
const { gltf, binaryChunk } = parseGLB(arrayBuffer);
|
|
1847
2011
|
const totalBuffers = Array.isArray(gltf.buffers) ? gltf.buffers.length : 0;
|
|
1848
2012
|
const buffers = new Array(totalBuffers);
|
|
@@ -2044,6 +2208,7 @@ export {
|
|
|
2044
2208
|
createUint8Bitmap,
|
|
2045
2209
|
decodeImageFromBuffer,
|
|
2046
2210
|
drawInfiniteGrid,
|
|
2211
|
+
encodePNG,
|
|
2047
2212
|
encodePNGToBuffer,
|
|
2048
2213
|
getDefaultRenderOptions,
|
|
2049
2214
|
hexToRgb,
|
|
@@ -2055,6 +2220,8 @@ export {
|
|
|
2055
2220
|
renderGLTFToPNGBuffer,
|
|
2056
2221
|
renderGLTFToPNGBufferFromGLBBuffer,
|
|
2057
2222
|
renderGLTFToPNGBufferFromURL,
|
|
2223
|
+
renderGLTFToPNGFromGLB,
|
|
2224
|
+
renderGLTFToPNGFromURL,
|
|
2058
2225
|
renderSceneFromGLTF,
|
|
2059
2226
|
resolveRenderOptions
|
|
2060
2227
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poppygl",
|
|
3
3
|
"main": "dist/index.js",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.24",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/tscircuit/poppygl.git"
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"typescript": "^5"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"fast-png": "^8.0.0",
|
|
40
41
|
"gl-matrix": "^3.4.4",
|
|
41
42
|
"pureimage": "^0.4.18",
|
|
42
43
|
"readable-stream": "^4.7.0"
|