@d5techs/3dgs-lib 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/3dgs-lib.js CHANGED
@@ -4411,7 +4411,8 @@ function readProperty$1(dataView, baseOffset, prop, littleEndian) {
4411
4411
  function sigmoid$1(x) {
4412
4412
  return 1 / (1 + Math.exp(-x));
4413
4413
  }
4414
- async function loadPLY(url) {
4414
+ async function loadPLY(url, options = {}) {
4415
+ const { coordinateSystem = "blender" } = options;
4415
4416
  const response = await fetch(url);
4416
4417
  if (!response.ok) {
4417
4418
  throw new Error(`无法加载 PLY 文件: ${url}`);
@@ -4423,6 +4424,7 @@ async function loadPLY(url) {
4423
4424
  throw new Error("不支持 ASCII 格式的 PLY 文件,请使用 binary_little_endian 或 binary_big_endian 格式");
4424
4425
  }
4425
4426
  const littleEndian = format === "binary_little_endian";
4427
+ const swapYZ = coordinateSystem === "blender";
4426
4428
  const propMap = buildPropertyMap(properties);
4427
4429
  const props = {
4428
4430
  x: propMap.get("x"),
@@ -4488,9 +4490,14 @@ async function loadPLY(url) {
4488
4490
  shRest[dstBase + 2] = srcB < shRestProps.length ? readProperty$1(dataView, base, shRestProps[srcB], littleEndian) : 0;
4489
4491
  }
4490
4492
  splats[i] = {
4491
- mean: [x, y, z],
4492
- scale: [scale_0, scale_1, scale_2],
4493
- rotation: [
4493
+ mean: swapYZ ? [x, z, y] : [x, y, z],
4494
+ scale: swapYZ ? [scale_0, scale_2, scale_1] : [scale_0, scale_1, scale_2],
4495
+ rotation: swapYZ ? [
4496
+ -rot_0 * qnorm,
4497
+ rot_1 * qnorm,
4498
+ rot_3 * qnorm,
4499
+ rot_2 * qnorm
4500
+ ] : [
4494
4501
  rot_0 * qnorm,
4495
4502
  rot_1 * qnorm,
4496
4503
  rot_2 * qnorm,
@@ -4706,7 +4713,8 @@ async function parsePLYBuffer(buffer, options = {}) {
4706
4713
  const {
4707
4714
  maxSplats = 2e5,
4708
4715
  loadSH = false,
4709
- onProgress
4716
+ onProgress,
4717
+ coordinateSystem = "blender"
4710
4718
  } = options;
4711
4719
  const seed = options.seed ?? buffer.byteLength;
4712
4720
  const { headerText, dataOffset } = extractHeader(buffer);
@@ -4796,27 +4804,41 @@ async function parsePLYBuffer(buffer, options = {}) {
4796
4804
  const opacities = new Float32Array(actualCount);
4797
4805
  const shCoeffs = loadSH ? new Float32Array(actualCount * 45) : void 0;
4798
4806
  const dataView = new DataView(buffer, dataOffset);
4807
+ const swapYZ = coordinateSystem === "blender";
4799
4808
  let outputIdx = 0;
4800
4809
  let lastProgress = 0;
4801
4810
  for (let i = 0; i < actualCount; i++) {
4802
4811
  const srcIdx = sampleIndices ? sampleIndices[i] : i;
4803
4812
  const base = srcIdx * stride;
4804
- positions[outputIdx * 3 + 0] = offsets.x >= 0 ? readProperty(dataView, base + offsets.x, types.x, littleEndian) : 0;
4805
- positions[outputIdx * 3 + 1] = offsets.y >= 0 ? readProperty(dataView, base + offsets.y, types.y, littleEndian) : 0;
4806
- positions[outputIdx * 3 + 2] = offsets.z >= 0 ? readProperty(dataView, base + offsets.z, types.z, littleEndian) : 0;
4807
- scales[outputIdx * 3 + 0] = offsets.scale_0 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_0, types.scale_0, littleEndian)) : 1;
4808
- scales[outputIdx * 3 + 1] = offsets.scale_1 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_1, types.scale_1, littleEndian)) : 1;
4809
- scales[outputIdx * 3 + 2] = offsets.scale_2 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_2, types.scale_2, littleEndian)) : 1;
4813
+ const px = offsets.x >= 0 ? readProperty(dataView, base + offsets.x, types.x, littleEndian) : 0;
4814
+ const py = offsets.y >= 0 ? readProperty(dataView, base + offsets.y, types.y, littleEndian) : 0;
4815
+ const pz = offsets.z >= 0 ? readProperty(dataView, base + offsets.z, types.z, littleEndian) : 0;
4816
+ positions[outputIdx * 3 + 0] = px;
4817
+ positions[outputIdx * 3 + 1] = swapYZ ? pz : py;
4818
+ positions[outputIdx * 3 + 2] = swapYZ ? py : pz;
4819
+ const sx = offsets.scale_0 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_0, types.scale_0, littleEndian)) : 1;
4820
+ const sy = offsets.scale_1 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_1, types.scale_1, littleEndian)) : 1;
4821
+ const sz = offsets.scale_2 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_2, types.scale_2, littleEndian)) : 1;
4822
+ scales[outputIdx * 3 + 0] = sx;
4823
+ scales[outputIdx * 3 + 1] = swapYZ ? sz : sy;
4824
+ scales[outputIdx * 3 + 2] = swapYZ ? sy : sz;
4810
4825
  const rot_0 = offsets.rot_0 >= 0 ? readProperty(dataView, base + offsets.rot_0, types.rot_0, littleEndian) : 1;
4811
4826
  const rot_1 = offsets.rot_1 >= 0 ? readProperty(dataView, base + offsets.rot_1, types.rot_1, littleEndian) : 0;
4812
4827
  const rot_2 = offsets.rot_2 >= 0 ? readProperty(dataView, base + offsets.rot_2, types.rot_2, littleEndian) : 0;
4813
4828
  const rot_3 = offsets.rot_3 >= 0 ? readProperty(dataView, base + offsets.rot_3, types.rot_3, littleEndian) : 0;
4814
4829
  const qlen = Math.sqrt(rot_0 * rot_0 + rot_1 * rot_1 + rot_2 * rot_2 + rot_3 * rot_3);
4815
4830
  const qnorm = qlen > 0 ? 1 / qlen : 1;
4816
- rotations[outputIdx * 4 + 0] = rot_0 * qnorm;
4817
- rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
4818
- rotations[outputIdx * 4 + 2] = rot_2 * qnorm;
4819
- rotations[outputIdx * 4 + 3] = rot_3 * qnorm;
4831
+ if (swapYZ) {
4832
+ rotations[outputIdx * 4 + 0] = -rot_0 * qnorm;
4833
+ rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
4834
+ rotations[outputIdx * 4 + 2] = rot_3 * qnorm;
4835
+ rotations[outputIdx * 4 + 3] = rot_2 * qnorm;
4836
+ } else {
4837
+ rotations[outputIdx * 4 + 0] = rot_0 * qnorm;
4838
+ rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
4839
+ rotations[outputIdx * 4 + 2] = rot_2 * qnorm;
4840
+ rotations[outputIdx * 4 + 3] = rot_3 * qnorm;
4841
+ }
4820
4842
  const f_dc_0 = offsets.f_dc_0 >= 0 ? readProperty(dataView, base + offsets.f_dc_0, types.f_dc_0, littleEndian) : 0;
4821
4843
  const f_dc_1 = offsets.f_dc_1 >= 0 ? readProperty(dataView, base + offsets.f_dc_1, types.f_dc_1, littleEndian) : 0;
4822
4844
  const f_dc_2 = offsets.f_dc_2 >= 0 ? readProperty(dataView, base + offsets.f_dc_2, types.f_dc_2, littleEndian) : 0;
@@ -13968,8 +13990,12 @@ class App {
13968
13990
  }
13969
13991
  /**
13970
13992
  * 加载 PLY 文件 (3D Gaussian Splatting)
13993
+ * @param urlOrBuffer PLY 文件 URL 或 ArrayBuffer
13994
+ * @param onProgress 进度回调
13995
+ * @param isLocalFile 是否为本地文件
13996
+ * @param coordinateSystem 源数据坐标系,默认 'blender'(Z-up → Y-up 自动转换)
13971
13997
  */
13972
- async addPLY(urlOrBuffer, onProgress, isLocalFile = false) {
13998
+ async addPLY(urlOrBuffer, onProgress, isLocalFile = false, coordinateSystem = "blender") {
13973
13999
  try {
13974
14000
  const isMobile = isMobileDevice();
13975
14001
  let buffer;
@@ -13998,7 +14024,8 @@ class App {
13998
14024
  const compactData = await this.parsePLYBuffer(buffer, {
13999
14025
  maxSplats: Infinity,
14000
14026
  loadSH: false,
14001
- onProgress: parseProgressCallback
14027
+ onProgress: parseProgressCallback,
14028
+ coordinateSystem
14002
14029
  });
14003
14030
  if (onProgress) onProgress(90, "upload");
14004
14031
  gsRenderer.setCompactData(compactData);
@@ -14012,7 +14039,8 @@ class App {
14012
14039
  const compactData = await this.parsePLYBuffer(buffer, {
14013
14040
  maxSplats: Infinity,
14014
14041
  loadSH: true,
14015
- onProgress: parseProgressCallback
14042
+ onProgress: parseProgressCallback,
14043
+ coordinateSystem
14016
14044
  });
14017
14045
  if (onProgress) onProgress(90, "upload");
14018
14046
  gsRenderer.setCompactData(compactData);