poppygl 0.0.14 → 0.0.15

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 CHANGED
@@ -35,6 +35,13 @@ interface GridOptions {
35
35
  y: number;
36
36
  z: number;
37
37
  }>;
38
+ infiniteGrid?: boolean;
39
+ cellSize?: number;
40
+ sectionSize?: number;
41
+ fadeDistance?: number;
42
+ fadeStrength?: number;
43
+ gridColor?: readonly [number, number, number];
44
+ sectionColor?: readonly [number, number, number];
38
45
  }
39
46
  interface GLTFResources {
40
47
  buffers: Uint8Array[];
@@ -80,6 +87,20 @@ declare function resolveRenderOptions(options?: RenderOptionsInput): RenderOptio
80
87
 
81
88
  declare function createGrid(options?: GridOptions): DrawCall;
82
89
 
90
+ /**
91
+ * Creates an infinite grid that matches the appearance of the 3d-viewer reference.
92
+ * This grid uses a special rendering approach with procedural generation and fading.
93
+ */
94
+ declare function createInfiniteGrid(options?: GridOptions): DrawCall & {
95
+ isInfiniteGrid: boolean;
96
+ cellSize: number;
97
+ sectionSize: number;
98
+ fadeDistance: number;
99
+ fadeStrength: number;
100
+ gridColor: readonly [number, number, number];
101
+ sectionColor: readonly [number, number, number];
102
+ };
103
+
83
104
  type GLTF = any;
84
105
  declare function createSceneFromGLTF(gltf: GLTF, resources: GLTFResources): GLTFScene;
85
106
 
@@ -130,6 +151,20 @@ declare class SoftwareRenderer {
130
151
  drawMesh(mesh: DrawCall, camera: Camera, light: LightSettings, material: Material, cullBackFaces?: boolean, gammaOut?: boolean): void;
131
152
  }
132
153
 
154
+ /**
155
+ * Draws an infinite grid with fade-out effect matching 3d-viewer appearance
156
+ */
157
+ declare function drawInfiniteGrid(software_renderer: SoftwareRenderer, params: {
158
+ camera: Camera;
159
+ cell_size?: number;
160
+ section_size?: number;
161
+ fade_distance?: number;
162
+ fade_strength?: number;
163
+ grid_color?: readonly [number, number, number];
164
+ section_color?: readonly [number, number, number];
165
+ gamma_out?: boolean;
166
+ }): void;
167
+
133
168
  declare const pureImageFactory: ImageFactory;
134
169
 
135
170
  declare function encodePNGToBuffer(image: BitmapLike): Promise<Buffer>;
@@ -145,4 +180,4 @@ declare function renderGLTFToPNGBuffer(gltfPathOrString: string, options?: Rende
145
180
 
146
181
  declare function renderSceneFromGLTF(scene: GLTFScene, options?: RenderOptionsInput, imageFactory?: ImageFactory): RenderResult;
147
182
 
148
- 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, createSceneFromGLTF, createUint8Bitmap, decodeImageFromBuffer, encodePNGToBuffer, getDefaultRenderOptions, isJPG, isPNG, loadGLTFWithResourcesFromURL, pureImageFactory, renderDrawCalls, renderGLTFToPNGBuffer, renderGLTFToPNGBufferFromGLBBuffer, renderGLTFToPNGBufferFromURL, renderSceneFromGLTF, resolveRenderOptions };
183
+ 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, isJPG, isPNG, loadGLTFWithResourcesFromURL, pureImageFactory, renderDrawCalls, renderGLTFToPNGBuffer, renderGLTFToPNGBufferFromGLBBuffer, renderGLTFToPNGBufferFromURL, renderSceneFromGLTF, resolveRenderOptions };
package/dist/index.js CHANGED
@@ -604,6 +604,142 @@ function resolveRenderOptions(options = {}) {
604
604
  };
605
605
  }
606
606
 
607
+ // lib/render/drawInfiniteGrid.ts
608
+ import { mat4 as mat44, vec4 as vec43 } from "gl-matrix";
609
+ function computeGridLineIntensity(params) {
610
+ const rx = params.world_x / params.cell_size;
611
+ const rz = params.world_z / params.cell_size;
612
+ const fract_x = rx - Math.floor(rx + 0.5);
613
+ const fract_z = rz - Math.floor(rz + 0.5);
614
+ const grid_x = Math.abs(fract_x);
615
+ const grid_z = Math.abs(fract_z);
616
+ const pixel_size_in_world = params.dist / Math.min(params.screen_width, params.screen_height);
617
+ const fwidth_x = pixel_size_in_world / params.cell_size;
618
+ const fwidth_z = pixel_size_in_world / params.cell_size;
619
+ const line_x = grid_x / (fwidth_x + 1e-4);
620
+ const line_z = grid_z / (fwidth_z + 1e-4);
621
+ const line = Math.min(line_x, line_z);
622
+ return 1 - Math.min(line * params.line_thickness_multiplier, 1);
623
+ }
624
+ function computeGridFadeAlpha(params) {
625
+ const fade_start = params.fade_distance;
626
+ const fade_end = params.fade_distance * params.fade_strength;
627
+ return 1 - Math.min(
628
+ 1,
629
+ Math.max(0, (params.dist - fade_start) / (fade_end - fade_start))
630
+ );
631
+ }
632
+ function drawInfiniteGrid(software_renderer, params) {
633
+ const cell_size = params.cell_size ?? 1;
634
+ const section_size = params.section_size ?? 10;
635
+ const fade_distance = params.fade_distance ?? 100;
636
+ const fade_strength = params.fade_strength ?? 1.5;
637
+ const grid_color = params.grid_color ?? [0.93, 0.93, 0.93];
638
+ const section_color = params.section_color ?? [0.8, 0.8, 1];
639
+ const gamma_out = params.gamma_out ?? true;
640
+ const camera = params.camera;
641
+ const view = camera.view;
642
+ const proj = camera.proj;
643
+ const inv_view = mat44.create();
644
+ mat44.invert(inv_view, view);
645
+ const cam_pos_x = inv_view[12];
646
+ const cam_pos_y = inv_view[13];
647
+ const cam_pos_z = inv_view[14];
648
+ const vp = mat44.create();
649
+ mat44.multiply(vp, proj, view);
650
+ const inv_vp = mat44.create();
651
+ mat44.invert(inv_vp, vp);
652
+ for (let y = 0; y < software_renderer.height; y++) {
653
+ for (let x = 0; x < software_renderer.width; x++) {
654
+ const ndc_x = x / (software_renderer.width - 1) * 2 - 1;
655
+ const ndc_y = 1 - y / (software_renderer.height - 1) * 2;
656
+ const near_point = vec43.fromValues(ndc_x, ndc_y, -1, 1);
657
+ const far_point = vec43.fromValues(ndc_x, ndc_y, 1, 1);
658
+ vec43.transformMat4(near_point, near_point, inv_vp);
659
+ vec43.transformMat4(far_point, far_point, inv_vp);
660
+ near_point[0] /= near_point[3];
661
+ near_point[1] /= near_point[3];
662
+ near_point[2] /= near_point[3];
663
+ far_point[0] /= far_point[3];
664
+ far_point[1] /= far_point[3];
665
+ far_point[2] /= far_point[3];
666
+ const ray_dir_x = far_point[0] - near_point[0];
667
+ const ray_dir_y = far_point[1] - near_point[1];
668
+ const ray_dir_z = far_point[2] - near_point[2];
669
+ if (Math.abs(ray_dir_y) < 1e-6) continue;
670
+ const t = -near_point[1] / ray_dir_y;
671
+ if (t < 0 || t > 1) continue;
672
+ const world_x = near_point[0] + t * ray_dir_x;
673
+ const world_z = near_point[2] + t * ray_dir_z;
674
+ const dx = world_x - cam_pos_x;
675
+ const dz = world_z - cam_pos_z;
676
+ const dist = Math.sqrt(dx * dx + dz * dz);
677
+ const alpha = computeGridFadeAlpha({
678
+ dist,
679
+ fade_distance,
680
+ fade_strength
681
+ });
682
+ if (alpha <= 0) continue;
683
+ const g1 = computeGridLineIntensity({
684
+ world_x,
685
+ world_z,
686
+ cell_size,
687
+ dist,
688
+ screen_width: software_renderer.width,
689
+ screen_height: software_renderer.height,
690
+ line_thickness_multiplier: 0.8
691
+ });
692
+ const g2 = computeGridLineIntensity({
693
+ world_x,
694
+ world_z,
695
+ cell_size: section_size,
696
+ dist,
697
+ screen_width: software_renderer.width,
698
+ screen_height: software_renderer.height,
699
+ line_thickness_multiplier: 0.5
700
+ // Thicker section lines
701
+ });
702
+ const grid_strength = Math.max(g1, g2);
703
+ if (grid_strength <= 0) continue;
704
+ const final_r = grid_color[0] * (1 - g2) + section_color[0] * g2;
705
+ const final_g = grid_color[1] * (1 - g2) + section_color[1] * g2;
706
+ const final_b = grid_color[2] * (1 - g2) + section_color[2] * g2;
707
+ const final_alpha = grid_strength * alpha;
708
+ if (final_alpha <= 0.01) continue;
709
+ let out_r = final_r;
710
+ let out_g = final_g;
711
+ let out_b = final_b;
712
+ if (gamma_out) {
713
+ out_r = srgbEncodeLinear01(clamp(out_r, 0, 1));
714
+ out_g = srgbEncodeLinear01(clamp(out_g, 0, 1));
715
+ out_b = srgbEncodeLinear01(clamp(out_b, 0, 1));
716
+ } else {
717
+ out_r = clamp(out_r, 0, 1);
718
+ out_g = clamp(out_g, 0, 1);
719
+ out_b = clamp(out_b, 0, 1);
720
+ }
721
+ const dst_idx = (y * software_renderer.width + x) * 4;
722
+ const dst_r = (software_renderer.buffer[dst_idx + 0] ?? 0) / 255;
723
+ const dst_g = (software_renderer.buffer[dst_idx + 1] ?? 0) / 255;
724
+ const dst_b = (software_renderer.buffer[dst_idx + 2] ?? 0) / 255;
725
+ const dst_a = (software_renderer.buffer[dst_idx + 3] ?? 0) / 255;
726
+ const one_minus_a = 1 - final_alpha;
727
+ const blend_r = out_r * final_alpha + dst_r * one_minus_a;
728
+ const blend_g = out_g * final_alpha + dst_g * one_minus_a;
729
+ const blend_b = out_b * final_alpha + dst_b * one_minus_a;
730
+ const blend_a = final_alpha + dst_a * one_minus_a;
731
+ software_renderer.setPixel(
732
+ x,
733
+ y,
734
+ clamp(blend_r, 0, 1) * 255 | 0,
735
+ clamp(blend_g, 0, 1) * 255 | 0,
736
+ clamp(blend_b, 0, 1) * 255 | 0,
737
+ clamp(blend_a, 0, 1) * 255 | 0
738
+ );
739
+ }
740
+ }
741
+ }
742
+
607
743
  // lib/render/renderDrawCalls.ts
608
744
  function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint8Bitmap) {
609
745
  const options = resolveRenderOptions(optionsInput);
@@ -632,31 +768,36 @@ function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint
632
768
  renderer.clear([0, 0, 0, 0]);
633
769
  }
634
770
  const allDrawCalls = [...drawCalls];
771
+ let infiniteGridOptions = null;
635
772
  if (options.grid) {
636
773
  const userGridOptions = typeof options.grid === "boolean" ? {} : options.grid;
637
- const aabb = computeWorldAABB(drawCalls);
638
- const sizeX = aabb.max[0] - aabb.min[0];
639
- const sizeZ = aabb.max[2] - aabb.min[2];
640
- const maxSize = Math.max(sizeX, sizeZ);
641
- const defaultSize = Math.ceil(maxSize * 1.2 / 2) * 2;
642
- const defaultOffset = {
643
- x: (aabb.min[0] + aabb.max[0]) / 2,
644
- y: aabb.min[1],
645
- z: (aabb.min[2] + aabb.max[2]) / 2
646
- };
647
- const defaultGridOptions = {
648
- size: defaultSize > 0 ? defaultSize : 10,
649
- offset: defaultOffset
650
- };
651
- const finalGridOptions = {
652
- ...defaultGridOptions,
653
- ...userGridOptions,
654
- offset: {
655
- ...defaultGridOptions.offset,
656
- ...userGridOptions.offset
657
- }
658
- };
659
- allDrawCalls.push(createGrid(finalGridOptions));
774
+ if (userGridOptions.infiniteGrid) {
775
+ infiniteGridOptions = userGridOptions;
776
+ } else {
777
+ const aabb = computeWorldAABB(drawCalls);
778
+ const sizeX = aabb.max[0] - aabb.min[0];
779
+ const sizeZ = aabb.max[2] - aabb.min[2];
780
+ const maxSize = Math.max(sizeX, sizeZ);
781
+ const defaultSize = Math.ceil(maxSize * 1.2 / 2) * 2;
782
+ const defaultOffset = {
783
+ x: (aabb.min[0] + aabb.max[0]) / 2,
784
+ y: aabb.min[1],
785
+ z: (aabb.min[2] + aabb.max[2]) / 2
786
+ };
787
+ const defaultGridOptions = {
788
+ size: defaultSize > 0 ? defaultSize : 10,
789
+ offset: defaultOffset
790
+ };
791
+ const finalGridOptions = {
792
+ ...defaultGridOptions,
793
+ ...userGridOptions,
794
+ offset: {
795
+ ...defaultGridOptions.offset,
796
+ ...userGridOptions.offset
797
+ }
798
+ };
799
+ allDrawCalls.push(createGrid(finalGridOptions));
800
+ }
660
801
  }
661
802
  const opaqueDraws = allDrawCalls.filter(
662
803
  (dc) => (dc.material.alphaMode ?? "OPAQUE") === "OPAQUE"
@@ -683,14 +824,77 @@ function renderDrawCalls(drawCalls, optionsInput = {}, imageFactory = createUint
683
824
  }
684
825
  }
685
826
  };
827
+ if (infiniteGridOptions) {
828
+ drawInfiniteGrid(renderer, {
829
+ camera,
830
+ cell_size: infiniteGridOptions.cellSize,
831
+ section_size: infiniteGridOptions.sectionSize,
832
+ fade_distance: infiniteGridOptions.fadeDistance,
833
+ fade_strength: infiniteGridOptions.fadeStrength,
834
+ grid_color: infiniteGridOptions.gridColor,
835
+ section_color: infiniteGridOptions.sectionColor,
836
+ gamma_out: options.gamma
837
+ });
838
+ }
686
839
  renderGroup(opaqueDraws);
687
840
  renderGroup(maskDraws);
688
841
  renderGroup(blendDraws);
689
842
  return { bitmap: renderer.bitmap, camera, options };
690
843
  }
691
844
 
845
+ // lib/gltf/createInfiniteGrid.ts
846
+ import { mat4 as mat45 } from "gl-matrix";
847
+ function createInfiniteGrid(options = {}) {
848
+ const cellSize = options.cellSize ?? 1;
849
+ const sectionSize = options.sectionSize ?? 10;
850
+ const fadeDistance = options.fadeDistance ?? 100;
851
+ const fadeStrength = options.fadeStrength ?? 1.5;
852
+ const gridColor = options.gridColor ?? [0.93, 0.93, 0.93];
853
+ const sectionColor = options.sectionColor ?? [0.8, 0.8, 1];
854
+ const size = 1e3;
855
+ const positions = new Float32Array([
856
+ -size,
857
+ 0,
858
+ -size,
859
+ size,
860
+ 0,
861
+ -size,
862
+ size,
863
+ 0,
864
+ size,
865
+ -size,
866
+ 0,
867
+ size
868
+ ]);
869
+ const indices = new Uint32Array([0, 1, 2, 0, 2, 3]);
870
+ const model = mat45.create();
871
+ const offset = options.offset ?? {};
872
+ mat45.fromTranslation(model, [offset.x ?? 0, offset.y ?? 0, offset.z ?? 0]);
873
+ return {
874
+ positions,
875
+ indices,
876
+ normals: null,
877
+ uvs: null,
878
+ model,
879
+ material: {
880
+ baseColorFactor: [1, 1, 1, 1],
881
+ baseColorTexture: null,
882
+ alphaMode: "BLEND"
883
+ },
884
+ mode: 4,
885
+ // triangles
886
+ isInfiniteGrid: true,
887
+ cellSize,
888
+ sectionSize,
889
+ fadeDistance,
890
+ fadeStrength,
891
+ gridColor,
892
+ sectionColor
893
+ };
894
+ }
895
+
692
896
  // lib/gltf/createSceneFromGLTF.ts
693
- import { mat4 as mat44 } from "gl-matrix";
897
+ import { mat4 as mat46 } from "gl-matrix";
694
898
  var COMPONENT_INFO = {
695
899
  5120: {
696
900
  name: "BYTE",
@@ -888,17 +1092,17 @@ function readIndices(ctx, accessorIndex) {
888
1092
  }
889
1093
  function nodeLocalMatrix(node) {
890
1094
  if (node.matrix) {
891
- const m2 = mat44.create();
1095
+ const m2 = mat46.create();
892
1096
  for (let i = 0; i < 16; i++) m2[i] = node.matrix[i];
893
1097
  return m2;
894
1098
  }
895
1099
  const translation = node.translation || [0, 0, 0];
896
1100
  const rotation = node.rotation || [0, 0, 0, 1];
897
1101
  const scale = node.scale || [1, 1, 1];
898
- const m = mat44.create();
899
- const tmp = mat44.create();
900
- mat44.fromRotationTranslationScale(tmp, rotation, translation, scale);
901
- mat44.copy(m, tmp);
1102
+ const m = mat46.create();
1103
+ const tmp = mat46.create();
1104
+ mat46.fromRotationTranslationScale(tmp, rotation, translation, scale);
1105
+ mat46.copy(m, tmp);
902
1106
  return m;
903
1107
  }
904
1108
  function getMaterial(gltf, textures, images, materialIndex) {
@@ -936,8 +1140,8 @@ function createSceneFromGLTF(gltf, resources) {
936
1140
  const node = nodes[nodeIndex];
937
1141
  if (!node) return;
938
1142
  const local = nodeLocalMatrix(node);
939
- const world = mat44.create();
940
- mat44.multiply(world, parentMatrix, local);
1143
+ const world = mat46.create();
1144
+ mat46.multiply(world, parentMatrix, local);
941
1145
  if (Number.isInteger(node.mesh)) {
942
1146
  const mesh = meshes[node.mesh];
943
1147
  if (mesh) {
@@ -975,7 +1179,7 @@ function createSceneFromGLTF(gltf, resources) {
975
1179
  traverse(childIndex, world);
976
1180
  }
977
1181
  }
978
- const identity = mat44.create();
1182
+ const identity = mat46.create();
979
1183
  for (const rootIndex of scene.nodes || []) {
980
1184
  traverse(rootIndex, identity);
981
1185
  }
@@ -1379,9 +1583,11 @@ export {
1379
1583
  computeSmoothNormals,
1380
1584
  computeWorldAABB,
1381
1585
  createGrid,
1586
+ createInfiniteGrid,
1382
1587
  createSceneFromGLTF,
1383
1588
  createUint8Bitmap,
1384
1589
  decodeImageFromBuffer,
1590
+ drawInfiniteGrid,
1385
1591
  encodePNGToBuffer,
1386
1592
  getDefaultRenderOptions,
1387
1593
  isJPG,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "poppygl",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.14",
4
+ "version": "0.0.15",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/tscircuit/poppygl.git"