@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.cjs CHANGED
@@ -4413,7 +4413,8 @@ function readProperty$1(dataView, baseOffset, prop, littleEndian) {
4413
4413
  function sigmoid$1(x) {
4414
4414
  return 1 / (1 + Math.exp(-x));
4415
4415
  }
4416
- async function loadPLY(url) {
4416
+ async function loadPLY(url, options = {}) {
4417
+ const { coordinateSystem = "blender" } = options;
4417
4418
  const response = await fetch(url);
4418
4419
  if (!response.ok) {
4419
4420
  throw new Error(`无法加载 PLY 文件: ${url}`);
@@ -4425,6 +4426,7 @@ async function loadPLY(url) {
4425
4426
  throw new Error("不支持 ASCII 格式的 PLY 文件,请使用 binary_little_endian 或 binary_big_endian 格式");
4426
4427
  }
4427
4428
  const littleEndian = format === "binary_little_endian";
4429
+ const swapYZ = coordinateSystem === "blender";
4428
4430
  const propMap = buildPropertyMap(properties);
4429
4431
  const props = {
4430
4432
  x: propMap.get("x"),
@@ -4490,9 +4492,14 @@ async function loadPLY(url) {
4490
4492
  shRest[dstBase + 2] = srcB < shRestProps.length ? readProperty$1(dataView, base, shRestProps[srcB], littleEndian) : 0;
4491
4493
  }
4492
4494
  splats[i] = {
4493
- mean: [x, y, z],
4494
- scale: [scale_0, scale_1, scale_2],
4495
- rotation: [
4495
+ mean: swapYZ ? [x, z, y] : [x, y, z],
4496
+ scale: swapYZ ? [scale_0, scale_2, scale_1] : [scale_0, scale_1, scale_2],
4497
+ rotation: swapYZ ? [
4498
+ -rot_0 * qnorm,
4499
+ rot_1 * qnorm,
4500
+ rot_3 * qnorm,
4501
+ rot_2 * qnorm
4502
+ ] : [
4496
4503
  rot_0 * qnorm,
4497
4504
  rot_1 * qnorm,
4498
4505
  rot_2 * qnorm,
@@ -4708,7 +4715,8 @@ async function parsePLYBuffer(buffer, options = {}) {
4708
4715
  const {
4709
4716
  maxSplats = 2e5,
4710
4717
  loadSH = false,
4711
- onProgress
4718
+ onProgress,
4719
+ coordinateSystem = "blender"
4712
4720
  } = options;
4713
4721
  const seed = options.seed ?? buffer.byteLength;
4714
4722
  const { headerText, dataOffset } = extractHeader(buffer);
@@ -4798,27 +4806,41 @@ async function parsePLYBuffer(buffer, options = {}) {
4798
4806
  const opacities = new Float32Array(actualCount);
4799
4807
  const shCoeffs = loadSH ? new Float32Array(actualCount * 45) : void 0;
4800
4808
  const dataView = new DataView(buffer, dataOffset);
4809
+ const swapYZ = coordinateSystem === "blender";
4801
4810
  let outputIdx = 0;
4802
4811
  let lastProgress = 0;
4803
4812
  for (let i = 0; i < actualCount; i++) {
4804
4813
  const srcIdx = sampleIndices ? sampleIndices[i] : i;
4805
4814
  const base = srcIdx * stride;
4806
- positions[outputIdx * 3 + 0] = offsets.x >= 0 ? readProperty(dataView, base + offsets.x, types.x, littleEndian) : 0;
4807
- positions[outputIdx * 3 + 1] = offsets.y >= 0 ? readProperty(dataView, base + offsets.y, types.y, littleEndian) : 0;
4808
- positions[outputIdx * 3 + 2] = offsets.z >= 0 ? readProperty(dataView, base + offsets.z, types.z, littleEndian) : 0;
4809
- scales[outputIdx * 3 + 0] = offsets.scale_0 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_0, types.scale_0, littleEndian)) : 1;
4810
- scales[outputIdx * 3 + 1] = offsets.scale_1 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_1, types.scale_1, littleEndian)) : 1;
4811
- scales[outputIdx * 3 + 2] = offsets.scale_2 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_2, types.scale_2, littleEndian)) : 1;
4815
+ const px = offsets.x >= 0 ? readProperty(dataView, base + offsets.x, types.x, littleEndian) : 0;
4816
+ const py = offsets.y >= 0 ? readProperty(dataView, base + offsets.y, types.y, littleEndian) : 0;
4817
+ const pz = offsets.z >= 0 ? readProperty(dataView, base + offsets.z, types.z, littleEndian) : 0;
4818
+ positions[outputIdx * 3 + 0] = px;
4819
+ positions[outputIdx * 3 + 1] = swapYZ ? pz : py;
4820
+ positions[outputIdx * 3 + 2] = swapYZ ? py : pz;
4821
+ const sx = offsets.scale_0 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_0, types.scale_0, littleEndian)) : 1;
4822
+ const sy = offsets.scale_1 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_1, types.scale_1, littleEndian)) : 1;
4823
+ const sz = offsets.scale_2 >= 0 ? Math.exp(readProperty(dataView, base + offsets.scale_2, types.scale_2, littleEndian)) : 1;
4824
+ scales[outputIdx * 3 + 0] = sx;
4825
+ scales[outputIdx * 3 + 1] = swapYZ ? sz : sy;
4826
+ scales[outputIdx * 3 + 2] = swapYZ ? sy : sz;
4812
4827
  const rot_0 = offsets.rot_0 >= 0 ? readProperty(dataView, base + offsets.rot_0, types.rot_0, littleEndian) : 1;
4813
4828
  const rot_1 = offsets.rot_1 >= 0 ? readProperty(dataView, base + offsets.rot_1, types.rot_1, littleEndian) : 0;
4814
4829
  const rot_2 = offsets.rot_2 >= 0 ? readProperty(dataView, base + offsets.rot_2, types.rot_2, littleEndian) : 0;
4815
4830
  const rot_3 = offsets.rot_3 >= 0 ? readProperty(dataView, base + offsets.rot_3, types.rot_3, littleEndian) : 0;
4816
4831
  const qlen = Math.sqrt(rot_0 * rot_0 + rot_1 * rot_1 + rot_2 * rot_2 + rot_3 * rot_3);
4817
4832
  const qnorm = qlen > 0 ? 1 / qlen : 1;
4818
- rotations[outputIdx * 4 + 0] = rot_0 * qnorm;
4819
- rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
4820
- rotations[outputIdx * 4 + 2] = rot_2 * qnorm;
4821
- rotations[outputIdx * 4 + 3] = rot_3 * qnorm;
4833
+ if (swapYZ) {
4834
+ rotations[outputIdx * 4 + 0] = -rot_0 * qnorm;
4835
+ rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
4836
+ rotations[outputIdx * 4 + 2] = rot_3 * qnorm;
4837
+ rotations[outputIdx * 4 + 3] = rot_2 * qnorm;
4838
+ } else {
4839
+ rotations[outputIdx * 4 + 0] = rot_0 * qnorm;
4840
+ rotations[outputIdx * 4 + 1] = rot_1 * qnorm;
4841
+ rotations[outputIdx * 4 + 2] = rot_2 * qnorm;
4842
+ rotations[outputIdx * 4 + 3] = rot_3 * qnorm;
4843
+ }
4822
4844
  const f_dc_0 = offsets.f_dc_0 >= 0 ? readProperty(dataView, base + offsets.f_dc_0, types.f_dc_0, littleEndian) : 0;
4823
4845
  const f_dc_1 = offsets.f_dc_1 >= 0 ? readProperty(dataView, base + offsets.f_dc_1, types.f_dc_1, littleEndian) : 0;
4824
4846
  const f_dc_2 = offsets.f_dc_2 >= 0 ? readProperty(dataView, base + offsets.f_dc_2, types.f_dc_2, littleEndian) : 0;
@@ -13970,8 +13992,12 @@ class App {
13970
13992
  }
13971
13993
  /**
13972
13994
  * 加载 PLY 文件 (3D Gaussian Splatting)
13995
+ * @param urlOrBuffer PLY 文件 URL 或 ArrayBuffer
13996
+ * @param onProgress 进度回调
13997
+ * @param isLocalFile 是否为本地文件
13998
+ * @param coordinateSystem 源数据坐标系,默认 'blender'(Z-up → Y-up 自动转换)
13973
13999
  */
13974
- async addPLY(urlOrBuffer, onProgress, isLocalFile = false) {
14000
+ async addPLY(urlOrBuffer, onProgress, isLocalFile = false, coordinateSystem = "blender") {
13975
14001
  try {
13976
14002
  const isMobile = isMobileDevice();
13977
14003
  let buffer;
@@ -14000,7 +14026,8 @@ class App {
14000
14026
  const compactData = await this.parsePLYBuffer(buffer, {
14001
14027
  maxSplats: Infinity,
14002
14028
  loadSH: false,
14003
- onProgress: parseProgressCallback
14029
+ onProgress: parseProgressCallback,
14030
+ coordinateSystem
14004
14031
  });
14005
14032
  if (onProgress) onProgress(90, "upload");
14006
14033
  gsRenderer.setCompactData(compactData);
@@ -14014,7 +14041,8 @@ class App {
14014
14041
  const compactData = await this.parsePLYBuffer(buffer, {
14015
14042
  maxSplats: Infinity,
14016
14043
  loadSH: true,
14017
- onProgress: parseProgressCallback
14044
+ onProgress: parseProgressCallback,
14045
+ coordinateSystem
14018
14046
  });
14019
14047
  if (onProgress) onProgress(90, "upload");
14020
14048
  gsRenderer.setCompactData(compactData);