@rings-webgpu/core 1.0.12 → 1.0.13

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.
@@ -22685,61 +22685,35 @@ struct InstanceData {
22685
22685
  /* wgsl */
22686
22686
  `
22687
22687
  #include "GlobalUniform"
22688
-
22689
- struct VSOut {
22690
- @location(auto) vColor : vec4f,
22691
- @location(auto) vTexCoord : vec2f,
22692
- @builtin(position) member : vec4f
22693
- };
22694
-
22695
- // ===== SPLAT CORE VS (from PlayCanvas shader-generator-gsplat.js) =====
22696
-
22697
- // Uniforms (mapped to WebGPU bindings)
22698
- // matrix_model, matrix_view, matrix_projection -> GlobalUniform + MaterialUniform
22699
- // viewport -> calculated from globalUniform.windowWidth/Height
22700
- // tex_params -> materialUniform.tex_params
22701
-
22702
- @group(1) @binding(0) var splatColor : texture_2d<f32>;
22703
- @group(1) @binding(1) var transformA : texture_2d<u32>;
22704
- @group(1) @binding(2) var transformB : texture_2d<f32>;
22705
- @group(1) @binding(4) var splatOrder : texture_2d<u32>;
22706
22688
 
22707
22689
  struct MaterialUniform {
22708
- tex_params: vec4f, // numSplats, textureWidth, validCount, visBoost
22709
22690
  modelMatrix: mat4x4<f32>,
22710
- pixelCull: vec4f, // minPixels, maxPixels, maxPixelCullDistance, reserved
22691
+ tex_params: vec4<f32>, // [numSplats, texWidth, validCount, visBoost]
22692
+ pixelCull: vec4<f32>, // [minPixels, maxPixels, maxPixelCullDistance, batchSize]
22711
22693
  };
22712
- @group(1) @binding(3) var<uniform> materialUniform : MaterialUniform;
22694
+ @group(1) @binding(0) var<uniform> materialUniform: MaterialUniform;
22713
22695
 
22714
- // Global variables (like PlayCanvas)
22715
- var<private> orderId: u32;
22716
- var<private> splatId: u32;
22696
+ struct VSOut {
22697
+ @builtin(position) member: vec4<f32>,
22698
+ @location(0) vColor: vec4<f32>,
22699
+ @location(1) vTexCoord: vec2<f32>,
22700
+ };
22701
+
22702
+ // Textures (like PlayCanvas)
22703
+ @group(1) @binding(1) var splatColor: texture_2d<f32>;
22704
+ @group(1) @binding(2) var transformA: texture_2d<u32>;
22705
+ @group(1) @binding(3) var transformB: texture_2d<f32>;
22706
+ @group(1) @binding(4) var splatOrder: texture_2d<u32>;
22707
+
22708
+ // Global variables for texture lookups
22717
22709
  var<private> splatUV: vec2<i32>;
22710
+ var<private> splatId: u32;
22718
22711
  var<private> tA: vec4<u32>;
22719
22712
 
22720
- // Helper: decode 16-bit half float
22721
- fn unpackHalf(h: u32) -> f32 {
22722
- let s = (h >> 15u) & 0x1u;
22723
- let e = (h >> 10u) & 0x1fu;
22724
- let m = h & 0x3ffu;
22725
- let sign = select(1.0, -1.0, s == 1u);
22726
- if (e == 0u) {
22727
- if (m == 0u) { return 0.0; }
22728
- return sign * (f32(m) * exp2(-24.0));
22729
- } else if (e == 31u) {
22730
- return sign * 65504.0;
22731
- } else {
22732
- return sign * (1.0 + f32(m) / 1024.0) * exp2(f32(i32(e) - 15));
22733
- }
22734
- }
22735
-
22736
22713
  // === calcSplatUV() - returns bool ===
22737
- fn calcSplatUV(instance_id: u32) -> bool {
22738
- let numSplats = u32(materialUniform.tex_params.x);
22714
+ fn calcSplatUV(orderId: u32) -> bool {
22739
22715
  let textureWidth = u32(materialUniform.tex_params.y);
22740
-
22741
- // calculate splat index
22742
- orderId = instance_id;
22716
+ let numSplats = u32(materialUniform.tex_params.x);
22743
22717
 
22744
22718
  if (orderId >= numSplats) {
22745
22719
  return false;
@@ -22786,6 +22760,19 @@ struct InstanceData {
22786
22760
  return result;
22787
22761
  }
22788
22762
 
22763
+ // === getRotationMatrix() - returns mat3x3 ===
22764
+ fn getRotationMatrix() -> mat3x3f {
22765
+ let cov_data = getCovariance();
22766
+ let covA = cov_data.covA;
22767
+ let covB = cov_data.covB;
22768
+
22769
+ return mat3x3f(
22770
+ vec3f(1.0 - 2.0 * (covA.z * covA.z + covB.x * covB.x), 2.0 * (covA.y * covA.z + covB.y * covB.x), 2.0 * (covA.y * covB.x - covB.y * covA.z)),
22771
+ vec3f(2.0 * (covA.y * covA.z - covB.y * covB.x), 1.0 - 2.0 * (covA.y * covA.y + covB.x * covB.x), 2.0 * (covA.z * covB.x + covA.y * covB.y)),
22772
+ vec3f(2.0 * (covA.y * covB.x + covB.y * covA.z), 2.0 * (covA.z * covB.x - covA.y * covB.y), 1.0 - 2.0 * (covA.y * covA.y + covA.z * covA.z))
22773
+ );
22774
+ }
22775
+
22789
22776
  // === calcV1V2() - returns vec4 ===
22790
22777
  fn calcV1V2(splat_cam: vec3f, covA: vec3f, covB: vec3f, W: mat3x3f, viewport: vec2f, projMat: mat4x4f) -> vec4f {
22791
22778
  let Vrk = mat3x3f(
@@ -22829,22 +22816,25 @@ struct InstanceData {
22829
22816
  @vertex
22830
22817
  fn VertMain(
22831
22818
  @builtin(vertex_index) vid : u32,
22832
- @builtin(instance_index) iid : u32
22819
+ @builtin(instance_index) iid : u32,
22820
+ @location(0) position: vec3<f32> // vertex_position from mesh (x, y, local_index)
22833
22821
  ) -> VSOut {
22834
22822
  var o: VSOut;
22835
22823
  let discardVec = vec4f(0.0, 0.0, 2.0, 1.0);
22836
22824
 
22837
- // Vertex position array (PlayCanvas uses attribute vec3 with x,y in [-1,1])
22838
- let vertex_position = array<vec2f, 4>(
22839
- vec2f(-2.0, -2.0),
22840
- vec2f( 2.0, -2.0),
22841
- vec2f(-2.0, 2.0),
22842
- vec2f( 2.0, 2.0)
22843
- );
22844
- let vertex_pos = vertex_position[vid & 3u];
22825
+ // Calculate splat ID
22826
+ // orderId = vertex_id_attrib + uint(vertex_position.z)
22827
+ // In our case: vertex_id_attrib = iid * batchSize
22828
+ let batchSize = u32(materialUniform.pixelCull.w);
22829
+ let base_splat_index = iid * batchSize;
22830
+ let local_splat_index = u32(position.z);
22831
+ let orderId = base_splat_index + local_splat_index;
22832
+
22833
+ // Use vertex position from mesh
22834
+ let vertex_pos = position.xy;
22845
22835
 
22846
22836
  // calculate splat uv
22847
- if (!calcSplatUV(iid)) {
22837
+ if (!calcSplatUV(orderId)) {
22848
22838
  o.member = discardVec;
22849
22839
  o.vColor = vec4f(0.0);
22850
22840
  o.vTexCoord = vec2f(0.0);
@@ -22872,9 +22862,8 @@ struct InstanceData {
22872
22862
  }
22873
22863
 
22874
22864
  // Frustum culling: cull splats outside screen bounds
22875
- // Add margin for splat radius (conservative: ~2x max splat size)
22876
22865
  let ndc = splat_proj.xyz / splat_proj.w;
22877
- let margin = 0.5; // Allow splats near edges to be visible
22866
+ let margin = 0.0;
22878
22867
  if (ndc.x < -1.0 - margin || ndc.x > 1.0 + margin ||
22879
22868
  ndc.y < -1.0 - margin || ndc.y > 1.0 + margin ||
22880
22869
  ndc.z < 0.0 || ndc.z > 1.0) {
@@ -22892,6 +22881,12 @@ struct InstanceData {
22892
22881
 
22893
22882
  // get color
22894
22883
  let color = textureLoad(splatColor, splatUV, 0);
22884
+ if (color.a < 1.0 / 255.0) {
22885
+ o.member = discardVec;
22886
+ o.vColor = vec4f(0.0);
22887
+ o.vTexCoord = vec2f(0.0);
22888
+ return o;
22889
+ }
22895
22890
 
22896
22891
  // calculate scale based on alpha
22897
22892
  let scale = min(1.0, sqrt(-log(1.0 / 255.0 / color.a)) / 2.0);
@@ -22900,7 +22895,7 @@ struct InstanceData {
22900
22895
  let visBoost = materialUniform.tex_params.w;
22901
22896
  var v1v2_scaled = v1v2 * scale * visBoost;
22902
22897
 
22903
- // Pixel coverage culling (min and max thresholds)
22898
+ // Pixel coverage culling
22904
22899
  let v1_len_sq = dot(v1v2_scaled.xy, v1v2_scaled.xy);
22905
22900
  let v2_len_sq = dot(v1v2_scaled.zw, v1v2_scaled.zw);
22906
22901
 
@@ -22908,7 +22903,7 @@ struct InstanceData {
22908
22903
  let maxPixels = materialUniform.pixelCull.y;
22909
22904
  let maxPixelCullDistance = materialUniform.pixelCull.z;
22910
22905
 
22911
- // Early out tiny splats (below minimum pixel coverage)
22906
+ // Early out tiny splats
22912
22907
  if (v1_len_sq < minPixels && v2_len_sq < minPixels) {
22913
22908
  o.member = discardVec;
22914
22909
  o.vColor = vec4f(0.0);
@@ -22916,13 +22911,9 @@ struct InstanceData {
22916
22911
  return o;
22917
22912
  }
22918
22913
 
22919
- // Cull oversized splats (above maximum pixel coverage)
22920
- // Only apply to splats close to camera (distance-based condition)
22914
+ // Cull oversized splats
22921
22915
  if (maxPixels > 0.0) {
22922
- // Calculate distance from splat to camera
22923
22916
  let splatDistance = length(splat_cam.xyz);
22924
-
22925
- // Only cull oversized splats if they are close to camera
22926
22917
  if (maxPixelCullDistance <= 0.0 || splatDistance < maxPixelCullDistance) {
22927
22918
  let maxAxisSq = maxPixels * maxPixels;
22928
22919
  if (v1_len_sq > maxAxisSq || v2_len_sq > maxAxisSq) {
@@ -22934,12 +22925,9 @@ struct InstanceData {
22934
22925
  }
22935
22926
  }
22936
22927
 
22937
- // gl_Position = splat_proj + vec4((vertex_position.x * v1v2.xy + vertex_position.y * v1v2.zw) / viewport * splat_proj.w, 0, 0);
22928
+ // Final position
22938
22929
  o.member = splat_proj + vec4f((vertex_pos.x * v1v2_scaled.xy + vertex_pos.y * v1v2_scaled.zw) / viewport * splat_proj.w, 0.0, 0.0);
22939
-
22940
- // texCoord = vertex_position.xy * scale / 2.0;
22941
22930
  o.vTexCoord = vertex_pos * scale / 2.0;
22942
-
22943
22931
  o.vColor = color;
22944
22932
 
22945
22933
  return o;
@@ -22954,27 +22942,25 @@ struct InstanceData {
22954
22942
  // === evalSplat() - like PlayCanvas splatCoreFS ===
22955
22943
  fn evalSplat(texCoord: vec2f, color: vec4f) -> vec4f {
22956
22944
  let A = dot(texCoord, texCoord);
22945
+ var B = exp(-A * 4.0) * color.a;
22957
22946
  if (A > 1.0) {
22958
- discard;
22947
+ B = 0.0;
22959
22948
  }
22960
22949
 
22961
- let B = exp(-A * 4.0) * color.a;
22962
22950
  if (B < 1.0 / 255.0) {
22963
- discard;
22951
+ B = 0.0;
22964
22952
  }
22965
22953
 
22966
- // TONEMAP_ENABLED branch not implemented (would call toneMap() and gammaCorrectOutput())
22967
22954
  return vec4f(color.rgb, B);
22968
22955
  }
22969
-
22970
- // === main() - like PlayCanvas splatMainFS ===
22956
+
22971
22957
  @fragment
22972
- fn FragMain(@location(auto) vColor: vec4f, @location(auto) vTexCoord: vec2f) -> FragmentOutput {
22973
- let result = evalSplat(vTexCoord, vColor);
22974
-
22958
+ fn FragMain(
22959
+ @location(0) vColor: vec4<f32>,
22960
+ @location(1) vTexCoord: vec2<f32>
22961
+ ) -> FragmentOutput {
22975
22962
  var o: FragmentOutput;
22976
- o.color = result;
22977
- o.gBuffer = vec4f(0.0);
22963
+ o.color = evalSplat(vTexCoord, vColor);
22978
22964
  return o;
22979
22965
  }
22980
22966
  `
@@ -23199,9 +23185,9 @@ struct InstanceData {
23199
23185
  const pass = new RenderShaderPass("gsplat_vs_dc", "gsplat_fs_dc");
23200
23186
  pass.passType = PassType.COLOR;
23201
23187
  pass.setShaderEntry("VertMain", "FragMain");
23202
- pass.topology = GPUPrimitiveTopology.triangle_strip;
23188
+ pass.topology = GPUPrimitiveTopology.triangle_list;
23203
23189
  pass.depthWriteEnabled = false;
23204
- pass.cullMode = "none";
23190
+ pass.cullMode = GPUCullMode.none;
23205
23191
  pass.shaderState.transparent = true;
23206
23192
  pass.shaderState.blendMode = BlendMode.NORMAL;
23207
23193
  pass.shaderState.writeMasks = [15, 15];
@@ -23223,6 +23209,7 @@ struct InstanceData {
23223
23209
  ], exports.GSplatShader);
23224
23210
 
23225
23211
  class GSplatMaterial extends Material {
23212
+ _pixelCullArray = new Float32Array(4);
23226
23213
  constructor() {
23227
23214
  super();
23228
23215
  ShaderLib.register("gsplat_vs_dc", GSplat_VS);
@@ -23238,6 +23225,7 @@ struct InstanceData {
23238
23225
  if (splatOrder) {
23239
23226
  pass.setTexture("splatOrder", splatOrder);
23240
23227
  }
23228
+ pass.shaderState.depthCompare = GPUCompareFunction.less;
23241
23229
  }
23242
23230
  /**
23243
23231
  * Set the model matrix for transforming splats to world space
@@ -23252,9 +23240,13 @@ struct InstanceData {
23252
23240
  * @param maxPixels Maximum pixel coverage (cull oversized splats), default: 0 (disabled)
23253
23241
  * @param maxPixelCullDistance Only cull oversized splats within this distance, 0 = always cull
23254
23242
  */
23255
- setPixelCulling(minPixels, maxPixels, maxPixelCullDistance = 0) {
23243
+ setPixelCulling(minPixels, maxPixels, maxPixelCullDistance = 0, batchSize = 128) {
23244
+ this._pixelCullArray[0] = minPixels;
23245
+ this._pixelCullArray[1] = maxPixels;
23246
+ this._pixelCullArray[2] = maxPixelCullDistance;
23247
+ this._pixelCullArray[3] = batchSize;
23256
23248
  const pass = this.shader.getDefaultColorShader();
23257
- pass.setUniform("pixelCull", new Float32Array([minPixels, maxPixels, maxPixelCullDistance, 0]));
23249
+ pass.setUniform("pixelCull", this._pixelCullArray);
23258
23250
  }
23259
23251
  }
23260
23252
 
@@ -23912,108 +23904,47 @@ struct InstanceData {
23912
23904
  }
23913
23905
  }
23914
23906
 
23915
- class PlaneGeometry extends GeometryBase {
23916
- width;
23917
- height;
23918
- segmentW;
23919
- segmentH;
23920
- up;
23921
- constructor(width, height, segmentW = 1, segmentH = 1, up = Vector3.Y_AXIS) {
23907
+ class GSplatGeometry extends GeometryBase {
23908
+ batchSize;
23909
+ constructor(batchSize = 128) {
23922
23910
  super();
23923
- this.width = width;
23924
- this.height = height;
23925
- this.segmentW = segmentW;
23926
- this.segmentH = segmentH;
23927
- this.up = up;
23928
- this.buildGeometry(this.up);
23929
- }
23930
- buildGeometry(axis) {
23931
- var x, y;
23932
- var numIndices;
23933
- var base;
23934
- var tw = this.segmentW + 1;
23935
- (this.segmentH + 1) * tw;
23936
- this.bounds = new BoundingBox(
23937
- Vector3.ZERO.clone(),
23938
- new Vector3(this.width, 1, this.height)
23939
- );
23940
- numIndices = this.segmentH * this.segmentW * 6;
23941
- let vertexCount = (this.segmentW + 1) * (this.segmentH + 1);
23942
- let position_arr = new Float32Array(vertexCount * 3);
23943
- let normal_arr = new Float32Array(vertexCount * 3);
23944
- let uv_arr = new Float32Array(vertexCount * 2);
23945
- let indices_arr;
23946
- let totalIndexCount = this.segmentW * this.segmentH * 2 * 3;
23947
- if (totalIndexCount >= Uint16Array.length) {
23948
- indices_arr = new Uint32Array(this.segmentW * this.segmentH * 2 * 3);
23949
- } else {
23950
- indices_arr = new Uint16Array(this.segmentW * this.segmentH * 2 * 3);
23951
- }
23952
- numIndices = 0;
23953
- var indexP = 0;
23954
- var indexN = 0;
23955
- var indexU = 0;
23956
- for (var yi = 0; yi <= this.segmentH; ++yi) {
23957
- for (var xi = 0; xi <= this.segmentW; ++xi) {
23958
- x = (xi / this.segmentW - 0.5) * this.width;
23959
- y = (yi / this.segmentH - 0.5) * this.height;
23960
- switch (axis) {
23961
- case Vector3.Y_AXIS:
23962
- position_arr[indexP++] = x;
23963
- position_arr[indexP++] = 0;
23964
- position_arr[indexP++] = y;
23965
- normal_arr[indexN++] = 0;
23966
- normal_arr[indexN++] = 1;
23967
- normal_arr[indexN++] = 0;
23968
- break;
23969
- case Vector3.Z_AXIS:
23970
- position_arr[indexP++] = x;
23971
- position_arr[indexP++] = -y;
23972
- position_arr[indexP++] = 0;
23973
- normal_arr[indexN++] = 0;
23974
- normal_arr[indexN++] = 0;
23975
- normal_arr[indexN++] = 1;
23976
- break;
23977
- case Vector3.X_AXIS:
23978
- position_arr[indexP++] = 0;
23979
- position_arr[indexP++] = x;
23980
- position_arr[indexP++] = y;
23981
- normal_arr[indexN++] = 1;
23982
- normal_arr[indexN++] = 0;
23983
- normal_arr[indexN++] = 0;
23984
- break;
23985
- default:
23986
- position_arr[indexP++] = x;
23987
- position_arr[indexP++] = 0;
23988
- position_arr[indexP++] = y;
23989
- normal_arr[indexN++] = 0;
23990
- normal_arr[indexN++] = 1;
23991
- normal_arr[indexN++] = 0;
23992
- break;
23993
- }
23994
- uv_arr[indexU++] = xi / this.segmentW;
23995
- uv_arr[indexU++] = yi / this.segmentH;
23996
- if (xi != this.segmentW && yi != this.segmentH) {
23997
- base = xi + yi * tw;
23998
- indices_arr[numIndices++] = base + 1;
23999
- indices_arr[numIndices++] = base;
24000
- indices_arr[numIndices++] = base + tw;
24001
- indices_arr[numIndices++] = base + 1;
24002
- indices_arr[numIndices++] = base + tw;
24003
- indices_arr[numIndices++] = base + tw + 1;
24004
- }
24005
- }
24006
- }
24007
- this.setIndices(indices_arr);
24008
- this.setAttribute(VertexAttributeName.position, position_arr);
24009
- this.setAttribute(VertexAttributeName.normal, normal_arr);
24010
- this.setAttribute(VertexAttributeName.uv, uv_arr);
24011
- this.setAttribute(VertexAttributeName.TEXCOORD_1, uv_arr);
23911
+ this.batchSize = batchSize;
23912
+ const meshPositions = new Float32Array(12 * batchSize);
23913
+ for (let i = 0; i < batchSize; ++i) {
23914
+ meshPositions.set([
23915
+ -2,
23916
+ -2,
23917
+ i,
23918
+ 2,
23919
+ -2,
23920
+ i,
23921
+ 2,
23922
+ 2,
23923
+ i,
23924
+ -2,
23925
+ 2,
23926
+ i
23927
+ ], i * 12);
23928
+ }
23929
+ const meshIndices = new Uint32Array(6 * batchSize);
23930
+ for (let i = 0; i < batchSize; ++i) {
23931
+ const b = i * 4;
23932
+ meshIndices.set([
23933
+ 0 + b,
23934
+ 1 + b,
23935
+ 2 + b,
23936
+ 0 + b,
23937
+ 2 + b,
23938
+ 3 + b
23939
+ ], i * 6);
23940
+ }
23941
+ this.setAttribute(VertexAttributeName.position, meshPositions);
23942
+ this.setIndices(meshIndices);
24012
23943
  this.addSubGeometry({
24013
23944
  indexStart: 0,
24014
- indexCount: indices_arr.length,
23945
+ indexCount: meshIndices.length,
24015
23946
  vertexStart: 0,
24016
- vertexCount: 0,
23947
+ vertexCount: meshPositions.length / 3,
24017
23948
  firstStart: 0,
24018
23949
  index: 0,
24019
23950
  topology: 0
@@ -24117,18 +24048,49 @@ struct InstanceData {
24117
24048
  updateTexture(width, height, data) {
24118
24049
  let device = webGPUContext.device;
24119
24050
  const bytesPerRow = width * 4 * 4;
24120
- const staging = device.createBuffer({
24051
+ device.queue.writeTexture(
24052
+ { texture: this.getGPUTexture() },
24053
+ data.buffer,
24054
+ { bytesPerRow },
24055
+ { width, height, depthOrArrayLayers: 1 }
24056
+ );
24057
+ }
24058
+ }
24059
+
24060
+ class R32UintTexture extends Texture {
24061
+ _dataBuffer;
24062
+ create(width, height, data) {
24063
+ let device = webGPUContext.device;
24064
+ const bytesPerRow = width * 4;
24065
+ this.format = GPUTextureFormat.r32uint;
24066
+ const mipmapCount = 1;
24067
+ this.createTextureDescriptor(width, height, mipmapCount, this.format);
24068
+ const textureDataBuffer = this._dataBuffer = device.createBuffer({
24121
24069
  size: data.byteLength,
24122
24070
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC
24123
24071
  });
24124
- device.queue.writeBuffer(staging, 0, data.buffer);
24072
+ device.queue.writeBuffer(textureDataBuffer, 0, data.buffer);
24125
24073
  const encoder = device.createCommandEncoder();
24126
24074
  encoder.copyBufferToTexture(
24127
- { buffer: staging, bytesPerRow },
24075
+ { buffer: textureDataBuffer, bytesPerRow },
24128
24076
  { texture: this.getGPUTexture() },
24129
24077
  { width, height, depthOrArrayLayers: 1 }
24130
24078
  );
24131
24079
  device.queue.submit([encoder.finish()]);
24080
+ this.samplerBindingLayout.type = `non-filtering`;
24081
+ this.textureBindingLayout.sampleType = `uint`;
24082
+ this.gpuSampler = device.createSampler({});
24083
+ return this;
24084
+ }
24085
+ updateTexture(width, height, data) {
24086
+ let device = webGPUContext.device;
24087
+ const bytesPerRow = width * 4;
24088
+ device.queue.writeTexture(
24089
+ { texture: this.getGPUTexture() },
24090
+ data.buffer,
24091
+ { bytesPerRow },
24092
+ { width, height, depthOrArrayLayers: 1 }
24093
+ );
24132
24094
  }
24133
24095
  }
24134
24096
 
@@ -24270,14 +24232,15 @@ struct InstanceData {
24270
24232
  // Web Worker for sorting
24271
24233
  _sortWorker;
24272
24234
  _lastSentTime = 0;
24273
- _minIntervalMs = 0;
24274
- // No throttle for immediate sorting
24235
+ _minIntervalMs = 16;
24275
24236
  _centersSent = false;
24276
24237
  _lastViewMatrixHash = 0;
24277
24238
  // Adaptive sorting optimization
24278
24239
  _lastCameraSpeed = 0;
24279
24240
  _adaptiveSorting = true;
24280
24241
  // Enable adaptive sorting by default
24242
+ _lastPixelCullParams = "";
24243
+ _texturesInitialized = false;
24281
24244
  // LOD (Level of Detail) system
24282
24245
  _lodEnabled = false;
24283
24246
  _lodDistances = [5, 10, 20, 40];
@@ -24299,6 +24262,11 @@ struct InstanceData {
24299
24262
  get fullCount() {
24300
24263
  return this._fullCount;
24301
24264
  }
24265
+ // Batched rendering
24266
+ _batchSize = 128;
24267
+ // Splats per draw call
24268
+ instanceCount = 0;
24269
+ // For InstanceDrawComponent compatibility
24302
24270
  constructor() {
24303
24271
  super();
24304
24272
  }
@@ -24314,24 +24282,20 @@ struct InstanceData {
24314
24282
  this.texParams = new Float32Array([this.count, this.size.x, this.count, 1]);
24315
24283
  this._positions = asset.position;
24316
24284
  const total = this.size.x * this.size.y;
24317
- this._orderData = new Uint32Array(total * 4);
24285
+ this._orderData = new Uint32Array(total);
24318
24286
  for (let i = 0; i < total; i++) {
24319
- const src = i < this.count ? i : this.count > 0 ? this.count - 1 : 0;
24320
- const base = i * 4;
24321
- this._orderData[base + 0] = src;
24322
- this._orderData[base + 1] = 0;
24323
- this._orderData[base + 2] = 0;
24324
- this._orderData[base + 3] = 0;
24325
- }
24326
- this.splatOrder = new Uint32ArrayTexture().create(this.size.x, this.size.y, this._orderData);
24287
+ this._orderData[i] = i < this.count ? i : this.count > 0 ? this.count - 1 : 0;
24288
+ }
24289
+ this.splatOrder = new R32UintTexture().create(this.size.x, this.size.y, this._orderData);
24327
24290
  this.splatOrder.name = "splatOrder";
24328
24291
  this.splatOrder.minFilter = "nearest";
24329
24292
  this.splatOrder.magFilter = "nearest";
24330
24293
  this.splatOrder.addressModeU = "clamp-to-edge";
24331
24294
  this.splatOrder.addressModeV = "clamp-to-edge";
24332
24295
  this.gsplatMaterial = new GSplatMaterial();
24333
- this.geometry = new PlaneGeometry(1, 1, 1, 1);
24296
+ this.geometry = new GSplatGeometry(this._batchSize);
24334
24297
  this.materials = [this.gsplatMaterial];
24298
+ this.instanceCount = 0;
24335
24299
  }
24336
24300
  /**
24337
24301
  * Update splat sorting before rendering
@@ -24408,12 +24372,7 @@ struct InstanceData {
24408
24372
  this.texParams[2] = Math.min(this.texParams[0], this.count);
24409
24373
  const total = this.size.x * this.size.y;
24410
24374
  for (let i = 0; i < total; i++) {
24411
- const src = i < this.count ? i : this.count > 0 ? this.count - 1 : 0;
24412
- const base = i * 4;
24413
- this._orderData[base + 0] = src;
24414
- this._orderData[base + 1] = 0;
24415
- this._orderData[base + 2] = 0;
24416
- this._orderData[base + 3] = 0;
24375
+ this._orderData[i] = i < this.count ? i : this.count > 0 ? this.count - 1 : 0;
24417
24376
  }
24418
24377
  this.splatOrder.updateTexture(this.size.x, this.size.y, this._orderData);
24419
24378
  if (this._sortWorker) {
@@ -24440,6 +24399,7 @@ struct InstanceData {
24440
24399
  } else {
24441
24400
  this._centersSent = false;
24442
24401
  }
24402
+ this.instanceCount = 0;
24443
24403
  }
24444
24404
  /**
24445
24405
  * Set visibility boost factor (material uniform tex_params.w)
@@ -24503,6 +24463,18 @@ struct InstanceData {
24503
24463
  distanceEnabled: this._maxPixelCullDistance > 0
24504
24464
  };
24505
24465
  }
24466
+ /**
24467
+ * Get batching statistics
24468
+ */
24469
+ getBatchingStats() {
24470
+ return {
24471
+ enabled: true,
24472
+ batchSize: this._batchSize,
24473
+ instanceCount: this.instanceCount,
24474
+ splatCount: this.count,
24475
+ reduction: this.count > 0 ? (1 - this.instanceCount / this.count) * 100 : 0
24476
+ };
24477
+ }
24506
24478
  /**
24507
24479
  * Calculate texture size for given splat count
24508
24480
  */
@@ -24725,18 +24697,20 @@ struct InstanceData {
24725
24697
  const indices = new Uint32Array(newOrder);
24726
24698
  const total = this.size.x * this.size.y;
24727
24699
  const count = this.count;
24728
- this._orderData = new Uint32Array(total * 4);
24729
- for (let i = 0; i < total; i++) {
24730
- const src = i < count ? indices[i] : count > 0 ? count - 1 : 0;
24731
- const base = i * 4;
24732
- this._orderData[base + 0] = src;
24733
- this._orderData[base + 1] = 0;
24734
- this._orderData[base + 2] = 0;
24735
- this._orderData[base + 3] = 0;
24700
+ if (!this._orderData || this._orderData.length !== total) {
24701
+ this._orderData = new Uint32Array(total);
24702
+ }
24703
+ const validCount = Math.min(count, indices.length);
24704
+ this._orderData.set(indices.subarray(0, validCount), 0);
24705
+ if (validCount < total) {
24706
+ const lastIndex = count > 0 ? count - 1 : 0;
24707
+ this._orderData.fill(lastIndex, validCount, total);
24736
24708
  }
24737
24709
  this.splatOrder.updateTexture(this.size.x, this.size.y, this._orderData);
24738
24710
  const valid = Math.max(0, Math.min(this.count, ev.data.count | 0));
24739
24711
  this.texParams[2] = valid;
24712
+ const newInstanceCount = Math.ceil(valid / this._batchSize);
24713
+ this.instanceCount = newInstanceCount;
24740
24714
  };
24741
24715
  const worldPos = this._worldPositions || this._positions;
24742
24716
  const centers = this._mapping ? new Float32Array(this._mapping.length * 3) : new Float32Array(worldPos);
@@ -24939,30 +24913,22 @@ struct InstanceData {
24939
24913
  nodeUpdate(view, passType, renderPassState, clusterLightingBuffer) {
24940
24914
  const worldMatrix = this.object3D.transform.worldMatrix;
24941
24915
  this.gsplatMaterial.setTransformMatrix(worldMatrix);
24942
- this.gsplatMaterial.setPixelCulling(this._minPixelCoverage, this._maxPixelCoverage, this._maxPixelCullDistance);
24943
- this.gsplatMaterial.setSplatTextures(
24944
- this.splatColor,
24945
- this.transformA,
24946
- this.transformB,
24947
- this.texParams,
24948
- this.splatOrder
24949
- );
24950
- super.nodeUpdate(view, passType, renderPassState, clusterLightingBuffer);
24951
- }
24952
- /**
24953
- * Render pass
24954
- */
24955
- renderPass2(view, passType, rendererPassState, clusterLightingBuffer, encoder, useBundle = false) {
24956
- for (let mat of this.materials) {
24957
- const passes = mat.getPass(passType);
24958
- if (!passes || passes.length === 0) continue;
24959
- for (const pass of passes) {
24960
- if (!pass.pipeline) continue;
24961
- pass.apply(this.geometry, rendererPassState);
24962
- GPUContext.bindPipeline(encoder, pass);
24963
- GPUContext.draw(encoder, 4, this.count, 0, 0);
24964
- }
24916
+ const currentParams = `${this._minPixelCoverage},${this._maxPixelCoverage},${this._maxPixelCullDistance},${this._batchSize}`;
24917
+ if (currentParams !== this._lastPixelCullParams) {
24918
+ this.gsplatMaterial.setPixelCulling(this._minPixelCoverage, this._maxPixelCoverage, this._maxPixelCullDistance, this._batchSize);
24919
+ this._lastPixelCullParams = currentParams;
24920
+ }
24921
+ if (!this._texturesInitialized) {
24922
+ this.gsplatMaterial.setSplatTextures(
24923
+ this.splatColor,
24924
+ this.transformA,
24925
+ this.transformB,
24926
+ this.texParams,
24927
+ this.splatOrder
24928
+ );
24929
+ this._texturesInitialized = true;
24965
24930
  }
24931
+ super.nodeUpdate(view, passType, renderPassState, clusterLightingBuffer);
24966
24932
  }
24967
24933
  /**
24968
24934
  * Render pass (fallback)
@@ -24976,7 +24942,28 @@ struct InstanceData {
24976
24942
  if (!pass.pipeline) continue;
24977
24943
  pass.apply(this.geometry, renderContext.rendererPassState || renderContext);
24978
24944
  GPUContext.bindPipeline(encoder, pass);
24979
- GPUContext.draw(encoder, 4, this.count, 0, 0);
24945
+ GPUContext.bindGeometryBuffer(encoder, this.geometry);
24946
+ const subGeometry = this.geometry.subGeometries[0];
24947
+ const lodInfo = subGeometry.lodLevels[0];
24948
+ if (this.instanceCount > 0) {
24949
+ GPUContext.drawIndexed(
24950
+ encoder,
24951
+ lodInfo.indexCount,
24952
+ this.instanceCount,
24953
+ lodInfo.indexStart,
24954
+ 0,
24955
+ 0
24956
+ );
24957
+ } else {
24958
+ GPUContext.drawIndexed(
24959
+ encoder,
24960
+ lodInfo.indexCount,
24961
+ 1,
24962
+ lodInfo.indexStart,
24963
+ 0,
24964
+ 0
24965
+ );
24966
+ }
24980
24967
  }
24981
24968
  }
24982
24969
  }
@@ -27512,6 +27499,115 @@ struct InstanceData {
27512
27499
  }
27513
27500
  }
27514
27501
 
27502
+ class PlaneGeometry extends GeometryBase {
27503
+ width;
27504
+ height;
27505
+ segmentW;
27506
+ segmentH;
27507
+ up;
27508
+ constructor(width, height, segmentW = 1, segmentH = 1, up = Vector3.Y_AXIS) {
27509
+ super();
27510
+ this.width = width;
27511
+ this.height = height;
27512
+ this.segmentW = segmentW;
27513
+ this.segmentH = segmentH;
27514
+ this.up = up;
27515
+ this.buildGeometry(this.up);
27516
+ }
27517
+ buildGeometry(axis) {
27518
+ var x, y;
27519
+ var numIndices;
27520
+ var base;
27521
+ var tw = this.segmentW + 1;
27522
+ (this.segmentH + 1) * tw;
27523
+ this.bounds = new BoundingBox(
27524
+ Vector3.ZERO.clone(),
27525
+ new Vector3(this.width, 1, this.height)
27526
+ );
27527
+ numIndices = this.segmentH * this.segmentW * 6;
27528
+ let vertexCount = (this.segmentW + 1) * (this.segmentH + 1);
27529
+ let position_arr = new Float32Array(vertexCount * 3);
27530
+ let normal_arr = new Float32Array(vertexCount * 3);
27531
+ let uv_arr = new Float32Array(vertexCount * 2);
27532
+ let indices_arr;
27533
+ let totalIndexCount = this.segmentW * this.segmentH * 2 * 3;
27534
+ if (totalIndexCount >= Uint16Array.length) {
27535
+ indices_arr = new Uint32Array(this.segmentW * this.segmentH * 2 * 3);
27536
+ } else {
27537
+ indices_arr = new Uint16Array(this.segmentW * this.segmentH * 2 * 3);
27538
+ }
27539
+ numIndices = 0;
27540
+ var indexP = 0;
27541
+ var indexN = 0;
27542
+ var indexU = 0;
27543
+ for (var yi = 0; yi <= this.segmentH; ++yi) {
27544
+ for (var xi = 0; xi <= this.segmentW; ++xi) {
27545
+ x = (xi / this.segmentW - 0.5) * this.width;
27546
+ y = (yi / this.segmentH - 0.5) * this.height;
27547
+ switch (axis) {
27548
+ case Vector3.Y_AXIS:
27549
+ position_arr[indexP++] = x;
27550
+ position_arr[indexP++] = 0;
27551
+ position_arr[indexP++] = y;
27552
+ normal_arr[indexN++] = 0;
27553
+ normal_arr[indexN++] = 1;
27554
+ normal_arr[indexN++] = 0;
27555
+ break;
27556
+ case Vector3.Z_AXIS:
27557
+ position_arr[indexP++] = x;
27558
+ position_arr[indexP++] = -y;
27559
+ position_arr[indexP++] = 0;
27560
+ normal_arr[indexN++] = 0;
27561
+ normal_arr[indexN++] = 0;
27562
+ normal_arr[indexN++] = 1;
27563
+ break;
27564
+ case Vector3.X_AXIS:
27565
+ position_arr[indexP++] = 0;
27566
+ position_arr[indexP++] = x;
27567
+ position_arr[indexP++] = y;
27568
+ normal_arr[indexN++] = 1;
27569
+ normal_arr[indexN++] = 0;
27570
+ normal_arr[indexN++] = 0;
27571
+ break;
27572
+ default:
27573
+ position_arr[indexP++] = x;
27574
+ position_arr[indexP++] = 0;
27575
+ position_arr[indexP++] = y;
27576
+ normal_arr[indexN++] = 0;
27577
+ normal_arr[indexN++] = 1;
27578
+ normal_arr[indexN++] = 0;
27579
+ break;
27580
+ }
27581
+ uv_arr[indexU++] = xi / this.segmentW;
27582
+ uv_arr[indexU++] = yi / this.segmentH;
27583
+ if (xi != this.segmentW && yi != this.segmentH) {
27584
+ base = xi + yi * tw;
27585
+ indices_arr[numIndices++] = base + 1;
27586
+ indices_arr[numIndices++] = base;
27587
+ indices_arr[numIndices++] = base + tw;
27588
+ indices_arr[numIndices++] = base + 1;
27589
+ indices_arr[numIndices++] = base + tw;
27590
+ indices_arr[numIndices++] = base + tw + 1;
27591
+ }
27592
+ }
27593
+ }
27594
+ this.setIndices(indices_arr);
27595
+ this.setAttribute(VertexAttributeName.position, position_arr);
27596
+ this.setAttribute(VertexAttributeName.normal, normal_arr);
27597
+ this.setAttribute(VertexAttributeName.uv, uv_arr);
27598
+ this.setAttribute(VertexAttributeName.TEXCOORD_1, uv_arr);
27599
+ this.addSubGeometry({
27600
+ indexStart: 0,
27601
+ indexCount: indices_arr.length,
27602
+ vertexStart: 0,
27603
+ vertexCount: 0,
27604
+ firstStart: 0,
27605
+ index: 0,
27606
+ topology: 0
27607
+ });
27608
+ }
27609
+ }
27610
+
27515
27611
  var __getOwnPropDesc$g = Object.getOwnPropertyDescriptor;
27516
27612
  var __decorateClass$g = (decorators, target, key, kind) => {
27517
27613
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$g(target, key) : target;
@@ -30486,7 +30582,15 @@ struct InstanceData {
30486
30582
  shadowMapPassRenderer;
30487
30583
  pointLightShadowRenderer;
30488
30584
  ddgiProbeRenderer;
30489
- postRenderer;
30585
+ _postRenderer;
30586
+ get postRenderer() {
30587
+ if (!this._postRenderer) {
30588
+ let gbufferFrame = GBufferFrame.getGBufferFrame("ColorPassGBuffer");
30589
+ this._postRenderer = this.addRenderer(PostRenderer);
30590
+ this._postRenderer.setRenderStates(gbufferFrame);
30591
+ }
30592
+ return this._postRenderer;
30593
+ }
30490
30594
  clusterLightingRender;
30491
30595
  reflectionRenderer;
30492
30596
  occlusionSystem;
@@ -30510,7 +30614,9 @@ struct InstanceData {
30510
30614
  }
30511
30615
  this.shadowMapPassRenderer = new ShadowMapPassRenderer();
30512
30616
  this.pointLightShadowRenderer = new PointLightShadowRenderer();
30513
- this.addPost(new FXAAPost());
30617
+ if (Engine3D.setting.render.postProcessing.fxaa.enable) {
30618
+ this.addPost(new FXAAPost());
30619
+ }
30514
30620
  }
30515
30621
  addRenderer(c, param) {
30516
30622
  let renderer;
@@ -30540,11 +30646,6 @@ struct InstanceData {
30540
30646
  this.pauseRender = false;
30541
30647
  }
30542
30648
  addPost(post) {
30543
- if (!this.postRenderer) {
30544
- let gbufferFrame = GBufferFrame.getGBufferFrame("ColorPassGBuffer");
30545
- this.postRenderer = this.addRenderer(PostRenderer);
30546
- this.postRenderer.setRenderStates(gbufferFrame);
30547
- }
30548
30649
  if (post instanceof PostBase) {
30549
30650
  this.postRenderer.attachPost(this.view, post);
30550
30651
  }
@@ -30562,15 +30663,21 @@ struct InstanceData {
30562
30663
  renderFrame() {
30563
30664
  let view = this._view;
30564
30665
  ProfilerUtil.startView(view);
30565
- GlobalBindGroup.getLightEntries(view.scene).update(view);
30566
- GlobalBindGroup.getReflectionEntries(view.scene).update(view);
30567
- this.occlusionSystem.update(view.camera, view.scene);
30568
- this.clusterLightingRender.render(view, this.occlusionSystem);
30569
- if (this.shadowMapPassRenderer) {
30666
+ if (this.clusterLightingRender) {
30667
+ GlobalBindGroup.getLightEntries(view.scene).update(view);
30668
+ GlobalBindGroup.getReflectionEntries(view.scene).update(view);
30669
+ }
30670
+ if (Engine3D.setting.occlusionQuery.enable && this.occlusionSystem) {
30671
+ this.occlusionSystem.update(view.camera, view.scene);
30672
+ }
30673
+ if (this.clusterLightingRender) {
30674
+ this.clusterLightingRender.render(view, this.occlusionSystem);
30675
+ }
30676
+ if (Engine3D.setting.shadow.enable && this.shadowMapPassRenderer) {
30570
30677
  ShadowLightsCollect.update(view);
30571
30678
  this.shadowMapPassRenderer.render(view, this.occlusionSystem);
30572
30679
  }
30573
- if (this.pointLightShadowRenderer) {
30680
+ if (Engine3D.setting.shadow.enable && this.pointLightShadowRenderer) {
30574
30681
  this.pointLightShadowRenderer.render(view, this.occlusionSystem);
30575
30682
  }
30576
30683
  if (this.depthPassRenderer) {
@@ -40791,7 +40898,7 @@ fn CsMain( @builtin(workgroup_id) workgroup_id : vec3<u32> , @builtin(global_inv
40791
40898
  }
40792
40899
  }
40793
40900
 
40794
- const version = "1.0.12";
40901
+ const version = "1.0.13";
40795
40902
 
40796
40903
  class Engine3D {
40797
40904
  /**
@@ -66579,6 +66686,7 @@ fn frag(){
66579
66686
  exports.GPUVertexFormat = GPUVertexFormat;
66580
66687
  exports.GPUVertexStepMode = GPUVertexStepMode;
66581
66688
  exports.GSplatFormat = GSplatFormat;
66689
+ exports.GSplatGeometry = GSplatGeometry;
66582
66690
  exports.GSplatMaterial = GSplatMaterial;
66583
66691
  exports.GSplat_FS = GSplat_FS;
66584
66692
  exports.GSplat_VS = GSplat_VS;
@@ -66827,6 +66935,7 @@ fn frag(){
66827
66935
  exports.Quad_frag_wgsl = Quad_frag_wgsl;
66828
66936
  exports.Quad_vert_wgsl = Quad_vert_wgsl;
66829
66937
  exports.Quaternion = Quaternion;
66938
+ exports.R32UintTexture = R32UintTexture;
66830
66939
  exports.RADIANS_TO_DEGREES = RADIANS_TO_DEGREES;
66831
66940
  exports.RGBEErrorCode = RGBEErrorCode;
66832
66941
  exports.RGBEHeader = RGBEHeader;