@d5techs/3dgs-lib 1.4.25 → 1.4.27
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 +215 -33
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +215 -33
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/App.d.ts +4 -0
- package/dist/core/OrbitControls.d.ts +1 -0
- package/dist/gs/SOGEncoder.d.ts +1 -1
- package/package.json +1 -1
package/dist/3dgs-lib.cjs
CHANGED
|
@@ -653,6 +653,8 @@ const _OrbitControls = class _OrbitControls {
|
|
|
653
653
|
__publicField(this, "lastTouchCenter", { x: 0, y: 0 });
|
|
654
654
|
// 启用/禁用
|
|
655
655
|
__publicField(this, "enabled", true);
|
|
656
|
+
// 双击拾取回调:返回世界坐标则聚焦,返回 null 则忽略
|
|
657
|
+
__publicField(this, "pickWorldPosition", null);
|
|
656
658
|
// 绑定的事件处理函数(用于移除监听器)
|
|
657
659
|
__publicField(this, "boundOnMouseDown");
|
|
658
660
|
__publicField(this, "boundOnMouseMove");
|
|
@@ -762,6 +764,9 @@ const _OrbitControls = class _OrbitControls {
|
|
|
762
764
|
if (this.enableDamping) {
|
|
763
765
|
this.velocityTheta += -deltaX * this.rotateSpeed;
|
|
764
766
|
this.velocityPhi += -deltaY * this.rotateSpeed;
|
|
767
|
+
const maxRotVel = 0.5;
|
|
768
|
+
this.velocityTheta = Math.max(-maxRotVel, Math.min(maxRotVel, this.velocityTheta));
|
|
769
|
+
this.velocityPhi = Math.max(-maxRotVel, Math.min(maxRotVel, this.velocityPhi));
|
|
765
770
|
} else {
|
|
766
771
|
this.theta -= deltaX * this.rotateSpeed;
|
|
767
772
|
this.phi -= deltaY * this.rotateSpeed;
|
|
@@ -780,9 +785,15 @@ const _OrbitControls = class _OrbitControls {
|
|
|
780
785
|
onWheel(e) {
|
|
781
786
|
e.preventDefault();
|
|
782
787
|
if (!this.enabled) return;
|
|
783
|
-
|
|
788
|
+
let delta = e.deltaY;
|
|
789
|
+
if (e.deltaMode === 1) delta *= 40;
|
|
790
|
+
else if (e.deltaMode === 2) delta *= 800;
|
|
791
|
+
const normalizedDelta = Math.sign(delta) * Math.min(Math.abs(delta), 150);
|
|
792
|
+
const zoomDelta = normalizedDelta * this.zoomSpeed;
|
|
784
793
|
if (this.enableDamping) {
|
|
785
794
|
this.velocityDistance += zoomDelta;
|
|
795
|
+
const maxVelocity = 2;
|
|
796
|
+
this.velocityDistance = Math.max(-maxVelocity, Math.min(maxVelocity, this.velocityDistance));
|
|
786
797
|
} else {
|
|
787
798
|
this.distance *= Math.exp(zoomDelta);
|
|
788
799
|
this.distance = Math.max(
|
|
@@ -839,6 +850,13 @@ const _OrbitControls = class _OrbitControls {
|
|
|
839
850
|
}
|
|
840
851
|
onDblClick(e) {
|
|
841
852
|
if (!this.enabled) return;
|
|
853
|
+
if (this.pickWorldPosition) {
|
|
854
|
+
const hit = this.pickWorldPosition(e.clientX, e.clientY);
|
|
855
|
+
if (hit) {
|
|
856
|
+
this.animateToTarget(hit);
|
|
857
|
+
}
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
842
860
|
const rect = this.canvas.getBoundingClientRect();
|
|
843
861
|
const ndcX = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
844
862
|
const ndcY = -((e.clientY - rect.top) / rect.height * 2 - 1);
|
|
@@ -6214,7 +6232,10 @@ function buildCodebook(values, k = 256) {
|
|
|
6214
6232
|
for (let i = 0; i < N; i++) {
|
|
6215
6233
|
const uniformPos = (sorted[i] - vMin) / vRange;
|
|
6216
6234
|
const quantilePos = i / N;
|
|
6217
|
-
const bin = Math.min(
|
|
6235
|
+
const bin = Math.min(
|
|
6236
|
+
H - 1,
|
|
6237
|
+
Math.floor(H * (beta * quantilePos + (1 - beta) * uniformPos))
|
|
6238
|
+
);
|
|
6218
6239
|
counts[bin]++;
|
|
6219
6240
|
sums[bin] += sorted[i];
|
|
6220
6241
|
}
|
|
@@ -6291,7 +6312,8 @@ function buildCodebook(values, k = 256) {
|
|
|
6291
6312
|
centroidValues.sort();
|
|
6292
6313
|
const result = new Array(k);
|
|
6293
6314
|
for (let i = 0; i < effectiveK; i++) result[i] = centroidValues[i];
|
|
6294
|
-
for (let i = effectiveK; i < k; i++)
|
|
6315
|
+
for (let i = effectiveK; i < k; i++)
|
|
6316
|
+
result[i] = centroidValues[effectiveK - 1];
|
|
6295
6317
|
return result;
|
|
6296
6318
|
}
|
|
6297
6319
|
function findNearest(sortedCB, value) {
|
|
@@ -6419,27 +6441,66 @@ async function gpuAssignLabels(device, pointsFlat, centroidsFlat, numPoints, num
|
|
|
6419
6441
|
const WORKGROUP_SIZE2 = 64;
|
|
6420
6442
|
const BATCH_SIZE = 1024 * WORKGROUP_SIZE2;
|
|
6421
6443
|
const numBatches = Math.ceil(numPoints / BATCH_SIZE);
|
|
6422
|
-
const shaderModule = device.createShaderModule({
|
|
6444
|
+
const shaderModule = device.createShaderModule({
|
|
6445
|
+
code: getGpuClusterShader(dim)
|
|
6446
|
+
});
|
|
6423
6447
|
const bindGroupLayout = device.createBindGroupLayout({
|
|
6424
6448
|
entries: [
|
|
6425
|
-
{
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6449
|
+
{
|
|
6450
|
+
binding: 0,
|
|
6451
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
6452
|
+
buffer: { type: "uniform" }
|
|
6453
|
+
},
|
|
6454
|
+
{
|
|
6455
|
+
binding: 1,
|
|
6456
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
6457
|
+
buffer: { type: "read-only-storage" }
|
|
6458
|
+
},
|
|
6459
|
+
{
|
|
6460
|
+
binding: 2,
|
|
6461
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
6462
|
+
buffer: { type: "read-only-storage" }
|
|
6463
|
+
},
|
|
6464
|
+
{
|
|
6465
|
+
binding: 3,
|
|
6466
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
6467
|
+
buffer: { type: "storage" }
|
|
6468
|
+
}
|
|
6429
6469
|
]
|
|
6430
6470
|
});
|
|
6431
6471
|
const pipeline = device.createComputePipeline({
|
|
6432
|
-
layout: device.createPipelineLayout({
|
|
6472
|
+
layout: device.createPipelineLayout({
|
|
6473
|
+
bindGroupLayouts: [bindGroupLayout]
|
|
6474
|
+
}),
|
|
6433
6475
|
compute: { module: shaderModule, entryPoint: "main" }
|
|
6434
6476
|
});
|
|
6435
|
-
const uniformBuf = device.createBuffer({
|
|
6436
|
-
|
|
6437
|
-
|
|
6477
|
+
const uniformBuf = device.createBuffer({
|
|
6478
|
+
size: 8,
|
|
6479
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
6480
|
+
});
|
|
6481
|
+
const centroidsBuf = device.createBuffer({
|
|
6482
|
+
size: centroidsFlat.byteLength,
|
|
6483
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
6484
|
+
});
|
|
6485
|
+
device.queue.writeBuffer(
|
|
6486
|
+
centroidsBuf,
|
|
6487
|
+
0,
|
|
6488
|
+
centroidsFlat.buffer
|
|
6489
|
+
);
|
|
6438
6490
|
const pointsBufSize = BATCH_SIZE * dim * 4;
|
|
6439
|
-
const pointsBuf = device.createBuffer({
|
|
6491
|
+
const pointsBuf = device.createBuffer({
|
|
6492
|
+
size: pointsBufSize,
|
|
6493
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
6494
|
+
});
|
|
6440
6495
|
const resultsBufSize = BATCH_SIZE * 4;
|
|
6441
|
-
const resultsBuf = device.createBuffer({
|
|
6442
|
-
|
|
6496
|
+
const resultsBuf = device.createBuffer({
|
|
6497
|
+
size: resultsBufSize,
|
|
6498
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
|
|
6499
|
+
});
|
|
6500
|
+
const readbackBuf = device.createBuffer({
|
|
6501
|
+
size: resultsBufSize,
|
|
6502
|
+
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
|
|
6503
|
+
});
|
|
6443
6504
|
const labels = new Uint32Array(numPoints);
|
|
6444
6505
|
const bindGroup = device.createBindGroup({
|
|
6445
6506
|
layout: bindGroupLayout,
|
|
@@ -6454,18 +6515,36 @@ async function gpuAssignLabels(device, pointsFlat, centroidsFlat, numPoints, num
|
|
|
6454
6515
|
const offset = batch * BATCH_SIZE;
|
|
6455
6516
|
const currentBatchSize = Math.min(numPoints - offset, BATCH_SIZE);
|
|
6456
6517
|
const groups = Math.ceil(currentBatchSize / WORKGROUP_SIZE2);
|
|
6457
|
-
device.queue.writeBuffer(
|
|
6458
|
-
|
|
6518
|
+
device.queue.writeBuffer(
|
|
6519
|
+
uniformBuf,
|
|
6520
|
+
0,
|
|
6521
|
+
new Uint32Array([currentBatchSize, numCentroids])
|
|
6522
|
+
);
|
|
6523
|
+
device.queue.writeBuffer(
|
|
6524
|
+
pointsBuf,
|
|
6525
|
+
0,
|
|
6526
|
+
pointsFlat.buffer,
|
|
6527
|
+
offset * dim * 4,
|
|
6528
|
+
currentBatchSize * dim * 4
|
|
6529
|
+
);
|
|
6459
6530
|
const encoder = device.createCommandEncoder();
|
|
6460
6531
|
const pass = encoder.beginComputePass();
|
|
6461
6532
|
pass.setPipeline(pipeline);
|
|
6462
6533
|
pass.setBindGroup(0, bindGroup);
|
|
6463
6534
|
pass.dispatchWorkgroups(groups);
|
|
6464
6535
|
pass.end();
|
|
6465
|
-
encoder.copyBufferToBuffer(
|
|
6536
|
+
encoder.copyBufferToBuffer(
|
|
6537
|
+
resultsBuf,
|
|
6538
|
+
0,
|
|
6539
|
+
readbackBuf,
|
|
6540
|
+
0,
|
|
6541
|
+
currentBatchSize * 4
|
|
6542
|
+
);
|
|
6466
6543
|
device.queue.submit([encoder.finish()]);
|
|
6467
6544
|
await readbackBuf.mapAsync(GPUMapMode.READ);
|
|
6468
|
-
const mapped = new Uint32Array(
|
|
6545
|
+
const mapped = new Uint32Array(
|
|
6546
|
+
readbackBuf.getMappedRange(0, currentBatchSize * 4)
|
|
6547
|
+
);
|
|
6469
6548
|
labels.set(mapped, offset);
|
|
6470
6549
|
readbackBuf.unmap();
|
|
6471
6550
|
}
|
|
@@ -6489,13 +6568,23 @@ async function kmeansGpu(pointsFlat, n, dim, k, iterations, onProgress) {
|
|
|
6489
6568
|
const centroids = new Float32Array(k * dim);
|
|
6490
6569
|
const indices = pickRandomIndices(n, k);
|
|
6491
6570
|
for (let i = 0; i < k; i++) {
|
|
6492
|
-
centroids.set(
|
|
6571
|
+
centroids.set(
|
|
6572
|
+
pointsFlat.subarray(indices[i] * dim, indices[i] * dim + dim),
|
|
6573
|
+
i * dim
|
|
6574
|
+
);
|
|
6493
6575
|
}
|
|
6494
6576
|
const device = await getGpuDevice();
|
|
6495
6577
|
for (let iter = 0; iter < iterations; iter++) {
|
|
6496
6578
|
onProgress == null ? void 0 : onProgress(iter, iterations);
|
|
6497
6579
|
if (device) {
|
|
6498
|
-
const newLabels = await gpuAssignLabels(
|
|
6580
|
+
const newLabels = await gpuAssignLabels(
|
|
6581
|
+
device,
|
|
6582
|
+
pointsFlat,
|
|
6583
|
+
centroids,
|
|
6584
|
+
n,
|
|
6585
|
+
k,
|
|
6586
|
+
dim
|
|
6587
|
+
);
|
|
6499
6588
|
labels.set(newLabels);
|
|
6500
6589
|
} else {
|
|
6501
6590
|
assignLabelsCpu(pointsFlat, centroids, labels, n, k, dim);
|
|
@@ -6504,7 +6593,14 @@ async function kmeansGpu(pointsFlat, n, dim, k, iterations, onProgress) {
|
|
|
6504
6593
|
}
|
|
6505
6594
|
onProgress == null ? void 0 : onProgress(iterations, iterations);
|
|
6506
6595
|
if (device) {
|
|
6507
|
-
const finalLabels = await gpuAssignLabels(
|
|
6596
|
+
const finalLabels = await gpuAssignLabels(
|
|
6597
|
+
device,
|
|
6598
|
+
pointsFlat,
|
|
6599
|
+
centroids,
|
|
6600
|
+
n,
|
|
6601
|
+
k,
|
|
6602
|
+
dim
|
|
6603
|
+
);
|
|
6508
6604
|
labels.set(finalLabels);
|
|
6509
6605
|
} else {
|
|
6510
6606
|
assignLabelsCpu(pointsFlat, centroids, labels, n, k, dim);
|
|
@@ -6550,7 +6646,8 @@ function updateCentroids(points, centroids, labels, n, k, dim) {
|
|
|
6550
6646
|
for (let c = 0; c < k; c++) {
|
|
6551
6647
|
if (counts[c] > 0) {
|
|
6552
6648
|
const off = c * dim;
|
|
6553
|
-
for (let d = 0; d < dim; d++)
|
|
6649
|
+
for (let d = 0; d < dim; d++)
|
|
6650
|
+
centroids[off + d] = sums[off + d] / counts[c];
|
|
6554
6651
|
} else {
|
|
6555
6652
|
const idx = Math.floor(Math.random() * n);
|
|
6556
6653
|
centroids.set(points.subarray(idx * dim, idx * dim + dim), c * dim);
|
|
@@ -6583,9 +6680,18 @@ function mortonSort(positions, count) {
|
|
|
6583
6680
|
const morton = new Uint32Array(count);
|
|
6584
6681
|
const indices = new Uint32Array(count);
|
|
6585
6682
|
for (let i = 0; i < count; i++) {
|
|
6586
|
-
const ix = Math.min(
|
|
6587
|
-
|
|
6588
|
-
|
|
6683
|
+
const ix = Math.min(
|
|
6684
|
+
1023,
|
|
6685
|
+
Math.floor(1024 * (positions[i * 3] - minx) / xlen)
|
|
6686
|
+
);
|
|
6687
|
+
const iy = Math.min(
|
|
6688
|
+
1023,
|
|
6689
|
+
Math.floor(1024 * (positions[i * 3 + 1] - miny) / ylen)
|
|
6690
|
+
);
|
|
6691
|
+
const iz = Math.min(
|
|
6692
|
+
1023,
|
|
6693
|
+
Math.floor(1024 * (positions[i * 3 + 2] - minz) / zlen)
|
|
6694
|
+
);
|
|
6589
6695
|
morton[i] = (part1By2(iz) << 2) + (part1By2(iy) << 1) + part1By2(ix);
|
|
6590
6696
|
indices[i] = i;
|
|
6591
6697
|
}
|
|
@@ -6722,7 +6828,8 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}, on
|
|
|
6722
6828
|
onProgress == null ? void 0 : onProgress("encode_scale", 0.2);
|
|
6723
6829
|
await yieldToUI();
|
|
6724
6830
|
const logScales = new Float32Array(count * 3);
|
|
6725
|
-
for (let i = 0; i < count * 3; i++)
|
|
6831
|
+
for (let i = 0; i < count * 3; i++)
|
|
6832
|
+
logScales[i] = Math.log(Math.max(1e-8, sSca[i]));
|
|
6726
6833
|
const scaleCodebook = buildCodebook(logScales, 256);
|
|
6727
6834
|
const scalesData = new Uint8ClampedArray(totalPixels * 4);
|
|
6728
6835
|
scalesData.fill(255);
|
|
@@ -6782,9 +6889,15 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}, on
|
|
|
6782
6889
|
r1 = qx;
|
|
6783
6890
|
r2 = qy;
|
|
6784
6891
|
}
|
|
6785
|
-
quatsData[off] = Math.round(
|
|
6786
|
-
|
|
6787
|
-
|
|
6892
|
+
quatsData[off] = Math.round(
|
|
6893
|
+
Math.max(0, Math.min(255, (r0 / SQRT2 + 0.5) * 255))
|
|
6894
|
+
);
|
|
6895
|
+
quatsData[off + 1] = Math.round(
|
|
6896
|
+
Math.max(0, Math.min(255, (r1 / SQRT2 + 0.5) * 255))
|
|
6897
|
+
);
|
|
6898
|
+
quatsData[off + 2] = Math.round(
|
|
6899
|
+
Math.max(0, Math.min(255, (r2 / SQRT2 + 0.5) * 255))
|
|
6900
|
+
);
|
|
6788
6901
|
quatsData[off + 3] = largest + 252;
|
|
6789
6902
|
}
|
|
6790
6903
|
onProgress == null ? void 0 : onProgress("encode_color", 0.4);
|
|
@@ -6840,7 +6953,8 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}, on
|
|
|
6840
6953
|
await yieldToUI();
|
|
6841
6954
|
const allCoeffValues = new Float32Array(effectivePaletteSize * shDim);
|
|
6842
6955
|
for (let c = 0; c < effectivePaletteSize; c++) {
|
|
6843
|
-
for (let d = 0; d < shDim; d++)
|
|
6956
|
+
for (let d = 0; d < shDim; d++)
|
|
6957
|
+
allCoeffValues[c * shDim + d] = centroids[c * shDim + d];
|
|
6844
6958
|
}
|
|
6845
6959
|
const shCodebook = buildCodebook(allCoeffValues, 256);
|
|
6846
6960
|
const centW = 64 * numCoeffs;
|
|
@@ -6854,8 +6968,14 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}, on
|
|
|
6854
6968
|
const off = (v * centW + u) * 4;
|
|
6855
6969
|
const cOff = n * shDim;
|
|
6856
6970
|
centData[off] = findNearest(shCodebook, centroids[cOff + c * 3]);
|
|
6857
|
-
centData[off + 1] = findNearest(
|
|
6858
|
-
|
|
6971
|
+
centData[off + 1] = findNearest(
|
|
6972
|
+
shCodebook,
|
|
6973
|
+
centroids[cOff + c * 3 + 1]
|
|
6974
|
+
);
|
|
6975
|
+
centData[off + 2] = findNearest(
|
|
6976
|
+
shCodebook,
|
|
6977
|
+
centroids[cOff + c * 3 + 2]
|
|
6978
|
+
);
|
|
6859
6979
|
centData[off + 3] = 255;
|
|
6860
6980
|
}
|
|
6861
6981
|
}
|
|
@@ -18113,6 +18233,7 @@ class App {
|
|
|
18113
18233
|
this.camera = new Camera();
|
|
18114
18234
|
this.camera.setAspect(this.renderer.getAspectRatio());
|
|
18115
18235
|
this.controls = new OrbitControls(this.camera, this.canvas);
|
|
18236
|
+
this.controls.pickWorldPosition = (cx, cy) => this.pickSplatPosition(cx, cy);
|
|
18116
18237
|
this.meshRenderer = new MeshRenderer(this.renderer, this.camera);
|
|
18117
18238
|
this.glbLoader = new GLBLoader(this.renderer.device);
|
|
18118
18239
|
this.objLoader = new OBJLoader(this.renderer.device);
|
|
@@ -18733,6 +18854,67 @@ class App {
|
|
|
18733
18854
|
getRenderer() {
|
|
18734
18855
|
return this.renderer;
|
|
18735
18856
|
}
|
|
18857
|
+
/**
|
|
18858
|
+
* CPU splat 拾取:在屏幕坐标 (clientX, clientY) 处找最近的 splat,返回世界坐标或 null
|
|
18859
|
+
*/
|
|
18860
|
+
pickSplatPosition(clientX, clientY) {
|
|
18861
|
+
const gsRenderer = this.sceneManager.getGSRenderer();
|
|
18862
|
+
if (!gsRenderer) return null;
|
|
18863
|
+
const positions = gsRenderer.getCPUPositions();
|
|
18864
|
+
if (!positions) return null;
|
|
18865
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
18866
|
+
const ndcX = (clientX - rect.left) / rect.width * 2 - 1;
|
|
18867
|
+
const ndcY = -((clientY - rect.top) / rect.height * 2 - 1);
|
|
18868
|
+
const modelMatrix = gsRenderer.getModelMatrix();
|
|
18869
|
+
const vp = this.camera.viewProjectionMatrix;
|
|
18870
|
+
const count = positions.length / 3;
|
|
18871
|
+
const step = count > 3e5 ? Math.ceil(count / 3e5) : 1;
|
|
18872
|
+
const halfW = rect.width / 2;
|
|
18873
|
+
const halfH = rect.height / 2;
|
|
18874
|
+
const pickRadiusPx = 20;
|
|
18875
|
+
const pickRadSq = pickRadiusPx * pickRadiusPx;
|
|
18876
|
+
let bestCamDistSq = Infinity;
|
|
18877
|
+
let bestIdx = -1;
|
|
18878
|
+
let bestScreenDistSq = Infinity;
|
|
18879
|
+
const m0 = modelMatrix[0], m1 = modelMatrix[1], m2 = modelMatrix[2];
|
|
18880
|
+
const m4 = modelMatrix[4], m5 = modelMatrix[5], m6 = modelMatrix[6];
|
|
18881
|
+
const m8 = modelMatrix[8], m9 = modelMatrix[9], m10 = modelMatrix[10];
|
|
18882
|
+
const m12 = modelMatrix[12], m13 = modelMatrix[13], m14 = modelMatrix[14];
|
|
18883
|
+
const v0 = vp[0], v1 = vp[1], v3 = vp[3];
|
|
18884
|
+
const v4 = vp[4], v5 = vp[5], v7 = vp[7];
|
|
18885
|
+
const v8 = vp[8], v9 = vp[9], v11 = vp[11];
|
|
18886
|
+
const v12 = vp[12], v13 = vp[13], v15 = vp[15];
|
|
18887
|
+
const rox = this.camera.position[0], roy = this.camera.position[1], roz = this.camera.position[2];
|
|
18888
|
+
for (let i = 0; i < count; i += step) {
|
|
18889
|
+
const i3 = i * 3;
|
|
18890
|
+
const lx = positions[i3], ly = positions[i3 + 1], lz = positions[i3 + 2];
|
|
18891
|
+
const tx = m0 * lx + m4 * ly + m8 * lz + m12;
|
|
18892
|
+
const ty = m1 * lx + m5 * ly + m9 * lz + m13;
|
|
18893
|
+
const tz = m2 * lx + m6 * ly + m10 * lz + m14;
|
|
18894
|
+
const cw = v3 * tx + v7 * ty + v11 * tz + v15;
|
|
18895
|
+
if (cw <= 0) continue;
|
|
18896
|
+
const invCw = 1 / cw;
|
|
18897
|
+
const pixX = ((v0 * tx + v4 * ty + v8 * tz + v12) * invCw - ndcX) * halfW;
|
|
18898
|
+
const pixY = ((v1 * tx + v5 * ty + v9 * tz + v13) * invCw - ndcY) * halfH;
|
|
18899
|
+
const screenDistSq = pixX * pixX + pixY * pixY;
|
|
18900
|
+
if (screenDistSq < pickRadSq) {
|
|
18901
|
+
const dx = tx - rox, dy = ty - roy, dz = tz - roz;
|
|
18902
|
+
const camDistSq = dx * dx + dy * dy + dz * dz;
|
|
18903
|
+
if (camDistSq < bestCamDistSq * 0.98 || camDistSq < bestCamDistSq * 1.02 && screenDistSq < bestScreenDistSq) {
|
|
18904
|
+
bestCamDistSq = camDistSq;
|
|
18905
|
+
bestScreenDistSq = screenDistSq;
|
|
18906
|
+
bestIdx = i;
|
|
18907
|
+
}
|
|
18908
|
+
}
|
|
18909
|
+
}
|
|
18910
|
+
if (bestIdx < 0) return null;
|
|
18911
|
+
const hx = positions[bestIdx * 3], hy = positions[bestIdx * 3 + 1], hz = positions[bestIdx * 3 + 2];
|
|
18912
|
+
return [
|
|
18913
|
+
m0 * hx + m4 * hy + m8 * hz + m12,
|
|
18914
|
+
m1 * hx + m5 * hy + m9 * hz + m13,
|
|
18915
|
+
m2 * hx + m6 * hy + m10 * hz + m14
|
|
18916
|
+
];
|
|
18917
|
+
}
|
|
18736
18918
|
getCamera() {
|
|
18737
18919
|
return this.camera;
|
|
18738
18920
|
}
|