poppygl 0.0.17 → 0.0.19

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 CHANGED
@@ -50,8 +50,13 @@ const png = await renderGLTFToPNGBufferFromGLBBuffer(glb, {
50
50
  `renderGLTFToPNGBufferFromURL` accepts the same render options as the lower-level APIs:
51
51
 
52
52
  - `width`/`height` (default `512`): output resolution in pixels.
53
+ - `supersampling`: render at `width * supersampling` / `height * supersampling`, then downsample (default `1`).
53
54
  - `fov`: vertical field of view in degrees (defaults to `35`).
54
55
  - `camPos` and `lookAt`: override the auto-framed camera position and target.
56
+ - `debugPoints`: optional `{ label, position }[]` overlay rendered on top of the final PNG for world-space debugging markers.
57
+ - `debugFontSize`: optional pixel size for `debugPoints` labels; when omitted the renderer derives a size from the image dimensions.
58
+ - `debugPointColor`: optional RGB tuple for debug point markers.
59
+ - `debugLabelColor`: optional RGB tuple for debug point labels.
55
60
  - `lightDir`: normalized directional light vector (defaults to a top-right key light).
56
61
  - `ambient`: ambient lighting contribution (0–1, defaults to `0.2`).
57
62
  - `gamma`: gamma correction applied to the output (defaults to `2.2`).
package/dist/index.d.ts CHANGED
@@ -53,6 +53,14 @@ interface GLTFScene {
53
53
  }
54
54
 
55
55
  declare const DEFAULT_LIGHT_DIR: readonly [-0.4, -0.9, -0.2];
56
+ interface DebugPoint {
57
+ label: string;
58
+ position: {
59
+ x: number;
60
+ y: number;
61
+ z: number;
62
+ };
63
+ }
56
64
  /**
57
65
  * Convert a hex color string to RGB array with values 0-1
58
66
  * @param hex - Hex color string like "#ffffff" or "ffffff"
@@ -62,6 +70,7 @@ declare function hexToRgb(hex: string): [number, number, number] | null;
62
70
  interface RenderOptions {
63
71
  width: number;
64
72
  height: number;
73
+ supersampling: number;
65
74
  fov: number;
66
75
  cull: boolean;
67
76
  gamma: boolean;
@@ -71,6 +80,10 @@ interface RenderOptions {
71
80
  lookAt?: readonly [number, number, number] | null;
72
81
  backgroundColor?: readonly [number, number, number] | string | null;
73
82
  grid?: boolean | GridOptions;
83
+ debugPoints?: DebugPoint[] | null;
84
+ debugFontSize?: number | null;
85
+ debugPointColor?: readonly [number, number, number] | null;
86
+ debugLabelColor?: readonly [number, number, number] | null;
74
87
  }
75
88
  type RenderOptionsInput = Partial<RenderOptions>;
76
89
  declare const DEFAULT_RENDER_OPTIONS: RenderOptions;
@@ -187,4 +200,4 @@ declare function renderGLTFToPNGBuffer(gltfPathOrString: string, options?: Rende
187
200
 
188
201
  declare function renderSceneFromGLTF(scene: GLTFScene, options?: RenderOptionsInput, imageFactory?: ImageFactory): RenderResult;
189
202
 
190
- export { type BitmapLike, type Camera, DEFAULT_LIGHT_DIR, DEFAULT_RENDER_OPTIONS, 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 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 };
203
+ export { type BitmapLike, type Camera, 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 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 };
package/dist/index.js CHANGED
@@ -132,35 +132,56 @@ var createUint8Bitmap = (width, height) => ({
132
132
  data: new Uint8ClampedArray(width * height * 4)
133
133
  });
134
134
 
135
- // lib/render/getDefaultRenderOptions.ts
136
- var DEFAULT_LIGHT_DIR = [-0.4, -0.9, -0.2];
137
- function hexToRgb(hex) {
138
- const cleanHex = hex.replace(/^#/, "");
139
- if (!/^[0-9A-Fa-f]{6}$/.test(cleanHex)) {
140
- return null;
135
+ // lib/render/downsampleBitmap.ts
136
+ function downsampleBitmap(source, targetWidth, targetHeight, imageFactory) {
137
+ const target = imageFactory(targetWidth, targetHeight);
138
+ const scaleX = source.width / targetWidth;
139
+ const scaleY = source.height / targetHeight;
140
+ for (let y = 0; y < targetHeight; y++) {
141
+ const srcY0 = Math.floor(y * scaleY);
142
+ const srcY1 = Math.max(
143
+ srcY0 + 1,
144
+ Math.min(source.height, Math.floor((y + 1) * scaleY))
145
+ );
146
+ for (let x = 0; x < targetWidth; x++) {
147
+ const srcX0 = Math.floor(x * scaleX);
148
+ const srcX1 = Math.max(
149
+ srcX0 + 1,
150
+ Math.min(source.width, Math.floor((x + 1) * scaleX))
151
+ );
152
+ let r = 0;
153
+ let g = 0;
154
+ let b = 0;
155
+ let a = 0;
156
+ let count = 0;
157
+ for (let sy = srcY0; sy < srcY1; sy++) {
158
+ for (let sx = srcX0; sx < srcX1; sx++) {
159
+ const srcIdx = (sy * source.width + sx) * 4;
160
+ r += source.data[srcIdx + 0];
161
+ g += source.data[srcIdx + 1];
162
+ b += source.data[srcIdx + 2];
163
+ a += source.data[srcIdx + 3];
164
+ count++;
165
+ }
166
+ }
167
+ const dstIdx = (y * targetWidth + x) * 4;
168
+ target.data[dstIdx + 0] = Math.round(r / count);
169
+ target.data[dstIdx + 1] = Math.round(g / count);
170
+ target.data[dstIdx + 2] = Math.round(b / count);
171
+ target.data[dstIdx + 3] = Math.round(a / count);
172
+ }
141
173
  }
142
- const r = Number.parseInt(cleanHex.substring(0, 2), 16) / 255;
143
- const g = Number.parseInt(cleanHex.substring(2, 4), 16) / 255;
144
- const b = Number.parseInt(cleanHex.substring(4, 6), 16) / 255;
145
- return [r, g, b];
146
- }
147
- var DEFAULT_RENDER_OPTIONS = {
148
- width: 800,
149
- height: 600,
150
- fov: 60,
151
- cull: true,
152
- gamma: true,
153
- ambient: 0.15,
154
- lightDir: DEFAULT_LIGHT_DIR,
155
- camPos: null,
156
- lookAt: null,
157
- backgroundColor: null,
158
- grid: false
159
- };
160
- function getDefaultRenderOptions() {
161
- return { ...DEFAULT_RENDER_OPTIONS };
174
+ return target;
162
175
  }
163
176
 
177
+ // lib/render/drawDebugPoints.ts
178
+ import {
179
+ glyphAdvanceRatio,
180
+ glyphLineAlphabet,
181
+ textMetrics
182
+ } from "@tscircuit/alphabet";
183
+ import { mat4 as mat44, vec4 as vec43 } from "gl-matrix";
184
+
164
185
  // lib/render/SoftwareRenderer.ts
165
186
  import { mat3, mat4 as mat43, vec3 as vec32, vec4 as vec42 } from "gl-matrix";
166
187
 
@@ -230,6 +251,40 @@ function computeSmoothNormals(positions, indices) {
230
251
  return normals;
231
252
  }
232
253
 
254
+ // lib/render/getDefaultRenderOptions.ts
255
+ var DEFAULT_LIGHT_DIR = [-0.4, -0.9, -0.2];
256
+ function hexToRgb(hex) {
257
+ const cleanHex = hex.replace(/^#/, "");
258
+ if (!/^[0-9A-Fa-f]{6}$/.test(cleanHex)) {
259
+ return null;
260
+ }
261
+ const r = Number.parseInt(cleanHex.substring(0, 2), 16) / 255;
262
+ const g = Number.parseInt(cleanHex.substring(2, 4), 16) / 255;
263
+ const b = Number.parseInt(cleanHex.substring(4, 6), 16) / 255;
264
+ return [r, g, b];
265
+ }
266
+ var DEFAULT_RENDER_OPTIONS = {
267
+ width: 800,
268
+ height: 600,
269
+ supersampling: 1,
270
+ fov: 60,
271
+ cull: true,
272
+ gamma: true,
273
+ ambient: 0.15,
274
+ lightDir: DEFAULT_LIGHT_DIR,
275
+ camPos: null,
276
+ lookAt: null,
277
+ backgroundColor: null,
278
+ grid: false,
279
+ debugPoints: null,
280
+ debugFontSize: null,
281
+ debugPointColor: null,
282
+ debugLabelColor: null
283
+ };
284
+ function getDefaultRenderOptions() {
285
+ return { ...DEFAULT_RENDER_OPTIONS };
286
+ }
287
+
233
288
  // lib/utils/mulColor.ts
234
289
  function mulColor(a, b) {
235
290
  return [a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]];
@@ -603,19 +658,177 @@ function edge(a, b, p) {
603
658
  return (p[0] - a[0]) * (b[1] - a[1]) - (p[1] - a[1]) * (b[0] - a[0]);
604
659
  }
605
660
 
606
- // lib/render/resolveRenderOptions.ts
607
- function resolveRenderOptions(options = {}) {
661
+ // lib/render/drawDebugPoints.ts
662
+ var DEFAULT_MARKER_COLOR = [
663
+ 255,
664
+ 0,
665
+ 170,
666
+ 255
667
+ ];
668
+ var DEFAULT_TEXT_COLOR = [
669
+ 255,
670
+ 24,
671
+ 170,
672
+ 255
673
+ ];
674
+ var CHARACTER_WIDTH_RATIO = 0.62;
675
+ function drawDebugPoints(renderer, camera, debugPoints, debugFontSize, debugPointColor, debugLabelColor) {
676
+ if (debugPoints.length === 0) return;
677
+ const viewProj = mat44.create();
678
+ mat44.multiply(viewProj, camera.proj, camera.view);
679
+ const markerColor = toColorRGBA(debugPointColor, DEFAULT_MARKER_COLOR);
680
+ const textColor = toColorRGBA(debugLabelColor, DEFAULT_TEXT_COLOR);
681
+ const labelScale = typeof debugFontSize === "number" && Number.isFinite(debugFontSize) ? Math.max(1, Math.round(debugFontSize)) : Math.max(
682
+ 12,
683
+ Math.round(Math.min(renderer.width, renderer.height) * 0.028)
684
+ );
685
+ const textThickness = Math.max(
686
+ 2,
687
+ Math.round(labelScale * textMetrics.strokeWidthRatio)
688
+ );
689
+ for (const debugPoint of debugPoints) {
690
+ const projected = projectWorldToScreen(
691
+ debugPoint.position,
692
+ viewProj,
693
+ renderer
694
+ );
695
+ if (!projected) continue;
696
+ drawMarker(renderer, projected, markerColor);
697
+ drawLabel(
698
+ renderer,
699
+ projected,
700
+ debugPoint.label,
701
+ textColor,
702
+ labelScale,
703
+ textThickness
704
+ );
705
+ }
706
+ }
707
+ function projectWorldToScreen(position, viewProj, renderer) {
708
+ const clip = vec43.fromValues(position.x, position.y, position.z, 1);
709
+ vec43.transformMat4(clip, clip, viewProj);
710
+ if (!Number.isFinite(clip[3]) || clip[3] <= 0) return null;
711
+ const invW = 1 / clip[3];
712
+ const ndcX = clip[0] * invW;
713
+ const ndcY = clip[1] * invW;
714
+ const ndcZ = clip[2] * invW;
715
+ if (!Number.isFinite(ndcX) || !Number.isFinite(ndcY) || !Number.isFinite(ndcZ) || ndcZ < -1 || ndcZ > 1) {
716
+ return null;
717
+ }
608
718
  return {
609
- ...DEFAULT_RENDER_OPTIONS,
610
- ...options,
611
- lightDir: options.lightDir != null ? options.lightDir : DEFAULT_RENDER_OPTIONS.lightDir,
612
- camPos: options.camPos !== void 0 ? options.camPos : DEFAULT_RENDER_OPTIONS.camPos,
613
- lookAt: options.lookAt !== void 0 ? options.lookAt : DEFAULT_RENDER_OPTIONS.lookAt
719
+ x: (ndcX * 0.5 + 0.5) * (renderer.width - 1),
720
+ y: (1 - (ndcY * 0.5 + 0.5)) * (renderer.height - 1)
614
721
  };
615
722
  }
723
+ function drawMarker(renderer, projected, color) {
724
+ const centerX = Math.round(projected.x);
725
+ const centerY = Math.round(projected.y);
726
+ for (let offset = -5; offset <= 4; offset += 1) {
727
+ renderer.setPixel(
728
+ centerX + offset,
729
+ centerY,
730
+ color[0],
731
+ color[1],
732
+ color[2],
733
+ color[3]
734
+ );
735
+ renderer.setPixel(
736
+ centerX,
737
+ centerY + offset,
738
+ color[0],
739
+ color[1],
740
+ color[2],
741
+ color[3]
742
+ );
743
+ }
744
+ }
745
+ function drawLabel(renderer, projected, label, color, scale, textThickness) {
746
+ if (label.length === 0) return;
747
+ const scaleX = scale * CHARACTER_WIDTH_RATIO;
748
+ const labelWidth = measureLabelWidth(label, scaleX);
749
+ let x = projected.x + scale * 0.6;
750
+ let y = projected.y - scale * 1.1;
751
+ if (x + labelWidth > renderer.width - 2) {
752
+ x = projected.x - scale * 0.6 - labelWidth;
753
+ }
754
+ if (y < 2) {
755
+ y = projected.y + scale * 0.2;
756
+ }
757
+ drawStrokeText(renderer, label, x, y, scaleX, scale, color, textThickness);
758
+ }
759
+ function drawStrokeText(renderer, text, x, y, scaleX, scaleY, color, thickness) {
760
+ let cursorX = x;
761
+ for (const rawChar of text) {
762
+ const char = resolveGlyph(rawChar);
763
+ const glyph = glyphLineAlphabet[char];
764
+ const advanceRatio = glyphAdvanceRatio[char] ?? (char === " " ? textMetrics.spaceWidthRatio : textMetrics.glyphWidthRatio);
765
+ if (glyph) {
766
+ for (const segment of glyph) {
767
+ drawLine(
768
+ renderer,
769
+ cursorX + segment.x1 * scaleX,
770
+ y + (1 - segment.y1) * scaleY,
771
+ cursorX + segment.x2 * scaleX,
772
+ y + (1 - segment.y2) * scaleY,
773
+ color,
774
+ thickness
775
+ );
776
+ }
777
+ }
778
+ cursorX += advanceRatio * scaleX;
779
+ }
780
+ }
781
+ function resolveGlyph(char) {
782
+ if (glyphLineAlphabet[char]) return char;
783
+ const uppercase = char.toUpperCase();
784
+ if (glyphLineAlphabet[uppercase]) return uppercase;
785
+ return "?";
786
+ }
787
+ function measureLabelWidth(text, scaleX) {
788
+ let width = 0;
789
+ for (const rawChar of text) {
790
+ const char = resolveGlyph(rawChar);
791
+ width += glyphAdvanceRatio[char] ?? (char === " " ? textMetrics.spaceWidthRatio : textMetrics.glyphWidthRatio);
792
+ }
793
+ return width * scaleX;
794
+ }
795
+ function drawLine(renderer, x0, y0, x1, y1, color, thickness) {
796
+ const dx = x1 - x0;
797
+ const dy = y1 - y0;
798
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
799
+ if (steps === 0) {
800
+ drawBrush(renderer, x0, y0, color, thickness);
801
+ return;
802
+ }
803
+ for (let step = 0; step <= steps; step += 1) {
804
+ const t = step / steps;
805
+ drawBrush(renderer, x0 + dx * t, y0 + dy * t, color, thickness);
806
+ }
807
+ }
808
+ function drawBrush(renderer, x, y, color, thickness) {
809
+ const radius = Math.max(0, Math.floor((thickness - 1) / 2));
810
+ const centerX = Math.round(x);
811
+ const centerY = Math.round(y);
812
+ for (let offsetY = -radius; offsetY <= radius; offsetY += 1) {
813
+ for (let offsetX = -radius; offsetX <= radius; offsetX += 1) {
814
+ renderer.setPixel(
815
+ centerX + offsetX,
816
+ centerY + offsetY,
817
+ color[0],
818
+ color[1],
819
+ color[2],
820
+ color[3]
821
+ );
822
+ }
823
+ }
824
+ }
825
+ function toColorRGBA(color, fallback) {
826
+ if (!color) return fallback;
827
+ return [color[0], color[1], color[2], 255];
828
+ }
616
829
 
617
830
  // lib/render/drawInfiniteGrid.ts
618
- import { mat4 as mat44, vec4 as vec43 } from "gl-matrix";
831
+ import { mat4 as mat45, vec4 as vec44 } from "gl-matrix";
619
832
  function computeGridLineIntensity(params) {
620
833
  const rx = params.world_x / params.cell_size;
621
834
  const rz = params.world_z / params.cell_size;
@@ -651,23 +864,23 @@ function drawInfiniteGrid(software_renderer, params) {
651
864
  const camera = params.camera;
652
865
  const view = camera.view;
653
866
  const proj = camera.proj;
654
- const inv_view = mat44.create();
655
- mat44.invert(inv_view, view);
867
+ const inv_view = mat45.create();
868
+ mat45.invert(inv_view, view);
656
869
  const cam_pos_x = inv_view[12];
657
870
  const cam_pos_y = inv_view[13];
658
871
  const cam_pos_z = inv_view[14];
659
- const vp = mat44.create();
660
- mat44.multiply(vp, proj, view);
661
- const inv_vp = mat44.create();
662
- mat44.invert(inv_vp, vp);
872
+ const vp = mat45.create();
873
+ mat45.multiply(vp, proj, view);
874
+ const inv_vp = mat45.create();
875
+ mat45.invert(inv_vp, vp);
663
876
  for (let y = 0; y < software_renderer.height; y++) {
664
877
  for (let x = 0; x < software_renderer.width; x++) {
665
878
  const ndc_x = x / (software_renderer.width - 1) * 2 - 1;
666
879
  const ndc_y = 1 - y / (software_renderer.height - 1) * 2;
667
- const near_point = vec43.fromValues(ndc_x, ndc_y, -1, 1);
668
- const far_point = vec43.fromValues(ndc_x, ndc_y, 1, 1);
669
- vec43.transformMat4(near_point, near_point, inv_vp);
670
- vec43.transformMat4(far_point, far_point, inv_vp);
880
+ const near_point = vec44.fromValues(ndc_x, ndc_y, -1, 1);
881
+ const far_point = vec44.fromValues(ndc_x, ndc_y, 1, 1);
882
+ vec44.transformMat4(near_point, near_point, inv_vp);
883
+ vec44.transformMat4(far_point, far_point, inv_vp);
671
884
  near_point[0] /= near_point[3];
672
885
  near_point[1] /= near_point[3];
673
886
  near_point[2] /= near_point[3];
@@ -682,9 +895,9 @@ function drawInfiniteGrid(software_renderer, params) {
682
895
  if (t < 0 || t > 1) continue;
683
896
  const world_x = near_point[0] + t * ray_dir_x;
684
897
  const world_z = near_point[2] + t * ray_dir_z;
685
- const world_pt = vec43.fromValues(world_x, grid_y, world_z, 1);
686
- const clip_pt = vec43.create();
687
- vec43.transformMat4(clip_pt, world_pt, vp);
898
+ const world_pt = vec44.fromValues(world_x, grid_y, world_z, 1);
899
+ const clip_pt = vec44.create();
900
+ vec44.transformMat4(clip_pt, world_pt, vp);
688
901
  const ndc_z = clip_pt[2] / clip_pt[3];
689
902
  const depth_01 = ndc_z * 0.5 + 0.5;
690
903
  const di = y * software_renderer.width + x;
@@ -759,22 +972,33 @@ function drawInfiniteGrid(software_renderer, params) {
759
972
  }
760
973
  }
761
974
 
975
+ // lib/render/resolveRenderOptions.ts
976
+ function resolveRenderOptions(options = {}) {
977
+ const supersampling = typeof options.supersampling === "number" && Number.isFinite(options.supersampling) ? Math.max(1, Math.floor(options.supersampling)) : DEFAULT_RENDER_OPTIONS.supersampling;
978
+ return {
979
+ ...DEFAULT_RENDER_OPTIONS,
980
+ ...options,
981
+ supersampling,
982
+ lightDir: options.lightDir != null ? options.lightDir : DEFAULT_RENDER_OPTIONS.lightDir,
983
+ camPos: options.camPos !== void 0 ? options.camPos : DEFAULT_RENDER_OPTIONS.camPos,
984
+ lookAt: options.lookAt !== void 0 ? options.lookAt : DEFAULT_RENDER_OPTIONS.lookAt
985
+ };
986
+ }
987
+
762
988
  // lib/render/renderDrawCalls.ts
763
989
  function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint8Bitmap) {
764
990
  const options = resolveRenderOptions(optionsInput);
991
+ const renderWidth = options.width * options.supersampling;
992
+ const renderHeight = options.height * options.supersampling;
765
993
  const camera = buildCamera(
766
994
  drawCalls,
767
- options.width,
768
- options.height,
995
+ renderWidth,
996
+ renderHeight,
769
997
  options.fov,
770
998
  options.camPos ?? null,
771
999
  options.lookAt ?? null
772
1000
  );
773
- const renderer = new SoftwareRenderer(
774
- options.width,
775
- options.height,
776
- imageFactory
777
- );
1001
+ const renderer = new SoftwareRenderer(renderWidth, renderHeight, imageFactory);
778
1002
  let rgb = null;
779
1003
  if (options.backgroundColor) {
780
1004
  if (typeof options.backgroundColor === "string") {
@@ -870,11 +1094,27 @@ function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint
870
1094
  });
871
1095
  }
872
1096
  renderGroup(blendDraws);
873
- return { bitmap: renderer.bitmap, camera, options };
1097
+ if (options.debugPoints?.length) {
1098
+ drawDebugPoints(
1099
+ renderer,
1100
+ camera,
1101
+ options.debugPoints,
1102
+ options.debugFontSize,
1103
+ options.debugPointColor,
1104
+ options.debugLabelColor
1105
+ );
1106
+ }
1107
+ const bitmap = options.supersampling > 1 ? downsampleBitmap(
1108
+ renderer.bitmap,
1109
+ options.width,
1110
+ options.height,
1111
+ imageFactory
1112
+ ) : renderer.bitmap;
1113
+ return { bitmap, camera, options };
874
1114
  }
875
1115
 
876
1116
  // lib/gltf/createInfiniteGrid.ts
877
- import { mat4 as mat45 } from "gl-matrix";
1117
+ import { mat4 as mat46 } from "gl-matrix";
878
1118
  function createInfiniteGrid(options = {}) {
879
1119
  const cellSize = options.cellSize ?? 1;
880
1120
  const sectionSize = options.sectionSize ?? 10;
@@ -898,9 +1138,9 @@ function createInfiniteGrid(options = {}) {
898
1138
  size
899
1139
  ]);
900
1140
  const indices = new Uint32Array([0, 1, 2, 0, 2, 3]);
901
- const model = mat45.create();
1141
+ const model = mat46.create();
902
1142
  const offset = options.offset ?? {};
903
- mat45.fromTranslation(model, [offset.x ?? 0, offset.y ?? 0, offset.z ?? 0]);
1143
+ mat46.fromTranslation(model, [offset.x ?? 0, offset.y ?? 0, offset.z ?? 0]);
904
1144
  return {
905
1145
  positions,
906
1146
  indices,
@@ -925,7 +1165,7 @@ function createInfiniteGrid(options = {}) {
925
1165
  }
926
1166
 
927
1167
  // lib/gltf/createSceneFromGLTF.ts
928
- import { mat4 as mat46 } from "gl-matrix";
1168
+ import { mat4 as mat47 } from "gl-matrix";
929
1169
  var COMPONENT_INFO = {
930
1170
  5120: {
931
1171
  name: "BYTE",
@@ -1123,17 +1363,17 @@ function readIndices(ctx, accessorIndex) {
1123
1363
  }
1124
1364
  function nodeLocalMatrix(node) {
1125
1365
  if (node.matrix) {
1126
- const m2 = mat46.create();
1366
+ const m2 = mat47.create();
1127
1367
  for (let i = 0; i < 16; i++) m2[i] = node.matrix[i];
1128
1368
  return m2;
1129
1369
  }
1130
1370
  const translation = node.translation || [0, 0, 0];
1131
1371
  const rotation = node.rotation || [0, 0, 0, 1];
1132
1372
  const scale = node.scale || [1, 1, 1];
1133
- const m = mat46.create();
1134
- const tmp = mat46.create();
1135
- mat46.fromRotationTranslationScale(tmp, rotation, translation, scale);
1136
- mat46.copy(m, tmp);
1373
+ const m = mat47.create();
1374
+ const tmp = mat47.create();
1375
+ mat47.fromRotationTranslationScale(tmp, rotation, translation, scale);
1376
+ mat47.copy(m, tmp);
1137
1377
  return m;
1138
1378
  }
1139
1379
  function getMaterial(gltf, textures, images, materialIndex) {
@@ -1171,8 +1411,8 @@ function createSceneFromGLTF(gltf, resources) {
1171
1411
  const node = nodes[nodeIndex];
1172
1412
  if (!node) return;
1173
1413
  const local = nodeLocalMatrix(node);
1174
- const world = mat46.create();
1175
- mat46.multiply(world, parentMatrix, local);
1414
+ const world = mat47.create();
1415
+ mat47.multiply(world, parentMatrix, local);
1176
1416
  if (Number.isInteger(node.mesh)) {
1177
1417
  const mesh = meshes[node.mesh];
1178
1418
  if (mesh) {
@@ -1210,7 +1450,7 @@ function createSceneFromGLTF(gltf, resources) {
1210
1450
  traverse(childIndex, world);
1211
1451
  }
1212
1452
  }
1213
- const identity = mat46.create();
1453
+ const identity = mat47.create();
1214
1454
  for (const rootIndex of scene.nodes || []) {
1215
1455
  traverse(rootIndex, identity);
1216
1456
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "poppygl",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.17",
4
+ "version": "0.0.19",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/tscircuit/poppygl.git"
@@ -20,6 +20,7 @@
20
20
  "type": "module",
21
21
  "devDependencies": {
22
22
  "@biomejs/biome": "^2.2.4",
23
+ "@tscircuit/alphabet": "^0.0.22",
23
24
  "@types/bun": "^1.2.23",
24
25
  "@types/readable-stream": "^4.0.21",
25
26
  "looks-same": "^10.0.1",