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