diff --git a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html
index 0ca0d98ad2e0..843b1d4f30e4 100644
--- a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html
+++ b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html
@@ -41,36 +41,72 @@
baseLayerPicker: false,
globe: false,
});
-
- // Enable rendering the sky
- viewer.scene.skyAtmosphere.show = true;
+ const scene = viewer.scene;
let tileset;
- // Add Photorealistic 3D Tiles
- try {
- tileset = await Cesium.createGooglePhotorealistic3DTileset(
- undefined,
- {
- enableDebugWireframe: true,
- }
- );
- viewer.scene.primitives.add(tileset);
- } catch (error) {
- console.log(`Error loading Photorealistic 3D Tiles tileset.
- ${error}`);
- }
+ const options = [
+ {
+ text: "Google P3DT",
+ onselect: async () => {
+ scene.primitives.remove(tileset);
+ try {
+ tileset = await Cesium.createGooglePhotorealistic3DTileset();
+ scene.primitives.add(tileset);
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ },
+ {
+ text: "Maxar OWT WFF 1.2",
+ onselect: async () => {
+ scene.primitives.remove(tileset);
+ try {
+ tileset = await Cesium.Cesium3DTileset.fromIonAssetId(691510, {
+ maximumScreenSpaceError: 4,
+ });
+ scene.primitives.add(tileset);
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ },
+ {
+ text: "Bentley BIM Model",
+ onselect: async () => {
+ scene.primitives.remove(tileset);
+ try {
+ tileset = await Cesium.Cesium3DTileset.fromIonAssetId(1240402);
+ scene.primitives.add(tileset);
+ viewer.zoomTo(tileset);
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ },
+ {
+ text: "Instanced",
+ onselect: async () => {
+ scene.primitives.remove(tileset);
+ try {
+ tileset = await Cesium.Cesium3DTileset.fromUrl(
+ "../../SampleData/Cesium3DTiles/Instanced/InstancedWithBatchTable/tileset.json"
+ );
+ scene.primitives.add(tileset);
+ viewer.zoomTo(tileset);
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ },
+ ];
- viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);
- const inspectorViewModel = viewer.cesium3DTilesInspector.viewModel;
- inspectorViewModel.tileset = tileset;
-
- const scene = viewer.scene;
+ Sandcastle.addDefaultToolbarMenu(options);
const scratchCartesian = new Cesium.Cartesian3();
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
const pickedPositionResult = scene.pickPosition(movement.position);
- console.log(pickedPositionResult);
if (Cesium.defined(pickedPositionResult)) {
viewer.entities.add({
position: pickedPositionResult,
@@ -83,12 +119,7 @@
}
const ray = scene.camera.getPickRay(movement.position);
- const picked = tileset.pick(
- ray,
- scene.frameState,
- true,
- scratchCartesian
- );
+ const picked = tileset.pick(ray, scene.frameState, scratchCartesian);
if (Cesium.defined(picked)) {
viewer.entities.add({
diff --git a/packages/engine/Source/Scene/Cesium3DTileContent.js b/packages/engine/Source/Scene/Cesium3DTileContent.js
index 76dcdd255b47..945242881b42 100644
--- a/packages/engine/Source/Scene/Cesium3DTileContent.js
+++ b/packages/engine/Source/Scene/Cesium3DTileContent.js
@@ -355,18 +355,12 @@ Cesium3DTileContent.prototype.update = function (tileset, frameState) {
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Cesium3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Cesium3DTileContent.prototype.pick = function (ray, frameState, result) {
DeveloperError.throwInstantiationError();
};
diff --git a/packages/engine/Source/Scene/Cesium3DTileset.js b/packages/engine/Source/Scene/Cesium3DTileset.js
index 3322452c427c..ac53d8ffb8b4 100644
--- a/packages/engine/Source/Scene/Cesium3DTileset.js
+++ b/packages/engine/Source/Scene/Cesium3DTileset.js
@@ -3427,24 +3427,19 @@ Cesium3DTileset.prototype.getHeight = function (cartographic, scene) {
};
const scratchSphereIntersection = new Interval();
+const scratchPickIntersection = new Cartesian3();
/**
* Find an intersection between a ray and the tileset surface that was rendered. The ray must be given in world coordinates.
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Cesium3DTileset.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Cesium3DTileset.prototype.pick = function (ray, frameState, result) {
const selectedTiles = this._selectedTiles;
const selectedLength = selectedTiles.length;
@@ -3461,7 +3456,11 @@ Cesium3DTileset.prototype.pick = function (
continue;
}
- const candidate = tile.content.pick(ray, frameState, cullBackFaces, result);
+ const candidate = tile.content.pick(
+ ray,
+ frameState,
+ scratchPickIntersection
+ );
if (!defined(candidate)) {
continue;
@@ -3469,7 +3468,7 @@ Cesium3DTileset.prototype.pick = function (
const distance = Cartesian3.distance(ray.origin, candidate);
if (distance < minDistance) {
- intersection = candidate;
+ intersection = Cartesian3.clone(candidate, result);
minDistance = distance;
}
}
@@ -3478,8 +3477,7 @@ Cesium3DTileset.prototype.pick = function (
return undefined;
}
- Cartesian3.clone(intersection, result);
- return result;
+ return intersection;
};
/**
diff --git a/packages/engine/Source/Scene/Composite3DTileContent.js b/packages/engine/Source/Scene/Composite3DTileContent.js
index 0925c389b785..02be6c08ac6c 100644
--- a/packages/engine/Source/Scene/Composite3DTileContent.js
+++ b/packages/engine/Source/Scene/Composite3DTileContent.js
@@ -353,18 +353,12 @@ Composite3DTileContent.prototype.update = function (tileset, frameState) {
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Composite3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Composite3DTileContent.prototype.pick = function (ray, frameState, result) {
if (!this._ready) {
return undefined;
}
@@ -375,7 +369,7 @@ Composite3DTileContent.prototype.pick = function (
const length = contents.length;
for (let i = 0; i < length; ++i) {
- const candidate = contents[i].pick(ray, frameState, cullBackFaces, result);
+ const candidate = contents[i].pick(ray, frameState, result);
if (!defined(candidate)) {
continue;
diff --git a/packages/engine/Source/Scene/Empty3DTileContent.js b/packages/engine/Source/Scene/Empty3DTileContent.js
index 8c2b22435a5d..0b5c4459da35 100644
--- a/packages/engine/Source/Scene/Empty3DTileContent.js
+++ b/packages/engine/Source/Scene/Empty3DTileContent.js
@@ -150,12 +150,7 @@ Empty3DTileContent.prototype.applyStyle = function (style) {};
Empty3DTileContent.prototype.update = function (tileset, frameState) {};
-Empty3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Empty3DTileContent.prototype.pick = function (ray, frameState, result) {
return undefined;
};
diff --git a/packages/engine/Source/Scene/Geometry3DTileContent.js b/packages/engine/Source/Scene/Geometry3DTileContent.js
index d13b21be91a0..d89a278eb4b1 100644
--- a/packages/engine/Source/Scene/Geometry3DTileContent.js
+++ b/packages/engine/Source/Scene/Geometry3DTileContent.js
@@ -525,12 +525,7 @@ Geometry3DTileContent.prototype.update = function (tileset, frameState) {
}
};
-Geometry3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Geometry3DTileContent.prototype.pick = function (ray, frameState, result) {
return undefined;
};
diff --git a/packages/engine/Source/Scene/Implicit3DTileContent.js b/packages/engine/Source/Scene/Implicit3DTileContent.js
index f3f8e6c9ff2f..96eebae51c1d 100644
--- a/packages/engine/Source/Scene/Implicit3DTileContent.js
+++ b/packages/engine/Source/Scene/Implicit3DTileContent.js
@@ -1177,12 +1177,7 @@ Implicit3DTileContent.prototype.applyStyle = function (style) {};
Implicit3DTileContent.prototype.update = function (tileset, frameState) {};
-Implicit3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Implicit3DTileContent.prototype.pick = function (ray, frameState, result) {
return undefined;
};
diff --git a/packages/engine/Source/Scene/Model/Model.js b/packages/engine/Source/Scene/Model/Model.js
index a781e442e6f7..d9a2f8c596c3 100644
--- a/packages/engine/Source/Scene/Model/Model.js
+++ b/packages/engine/Source/Scene/Model/Model.js
@@ -2489,14 +2489,13 @@ Model.prototype.isClippingEnabled = function () {
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Model.prototype.pick = function (ray, frameState, cullBackFaces, result) {
- return pickModel(this, ray, frameState, cullBackFaces, result);
+Model.prototype.pick = function (ray, frameState, result) {
+ return pickModel(this, ray, frameState, result);
};
/**
diff --git a/packages/engine/Source/Scene/Model/Model3DTileContent.js b/packages/engine/Source/Scene/Model/Model3DTileContent.js
index c0e71069a2c4..11146fe92d8e 100644
--- a/packages/engine/Source/Scene/Model/Model3DTileContent.js
+++ b/packages/engine/Source/Scene/Model/Model3DTileContent.js
@@ -421,23 +421,17 @@ Model3DTileContent.fromGeoJson = async function (
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Model3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Model3DTileContent.prototype.pick = function (ray, frameState, result) {
if (!defined(this._model) || !this._ready) {
return undefined;
}
- return this._model.pick(ray, frameState, cullBackFaces, result);
+ return this._model.pick(ray, frameState, result);
};
function makeModelOptions(tileset, tile, content, additionalOptions) {
diff --git a/packages/engine/Source/Scene/Model/pickModel.js b/packages/engine/Source/Scene/Model/pickModel.js
index de0be7e8cb98..16a2bb02f94f 100644
--- a/packages/engine/Source/Scene/Model/pickModel.js
+++ b/packages/engine/Source/Scene/Model/pickModel.js
@@ -1,4 +1,5 @@
import AttributeCompression from "../../Core/AttributeCompression.js";
+import BoundingSphere from "../../Core/BoundingSphere.js";
import Cartesian3 from "../../Core/Cartesian3.js";
import Cartographic from "../../Core/Cartographic.js";
import Check from "../../Core/Check.js";
@@ -18,9 +19,11 @@ import ModelUtility from "./ModelUtility.js";
const scratchV0 = new Cartesian3();
const scratchV1 = new Cartesian3();
const scratchV2 = new Cartesian3();
+const scratchNodeComputedTransform = new Matrix4();
const scratchModelMatrix = new Matrix4();
+const scratchcomputedModelMatrix = new Matrix4();
const scratchPickCartographic = new Cartographic();
-const scratchInstanceMatrix = new Matrix4();
+const scratchBoundingSphere = new BoundingSphere();
/**
* Find an intersection between a ray and the model surface that was rendered. The ray must be given in world coordinates.
@@ -28,19 +31,12 @@ const scratchInstanceMatrix = new Matrix4();
* @param {Model} model The model to pick.
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-export default function pickModel(
- model,
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+export default function pickModel(model, ray, frameState, result) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("model", model);
Check.typeOf.object("ray", ray);
@@ -59,8 +55,14 @@ export default function pickModel(
const runtimeNode = nodes[i];
const node = runtimeNode.node;
- let nodeComputedTransform = runtimeNode.computedTransform;
- let modelMatrix = sceneGraph.computedModelMatrix;
+ let nodeComputedTransform = Matrix4.clone(
+ runtimeNode.computedTransform,
+ scratchNodeComputedTransform
+ );
+ let modelMatrix = Matrix4.clone(
+ sceneGraph.computedModelMatrix,
+ scratchModelMatrix
+ );
const instances = node.instances;
if (defined(instances)) {
@@ -77,38 +79,23 @@ export default function pickModel(
runtimeNode.computedTransform,
nodeComputedTransform
);
- } else {
- // The node transform should be pre-multiplied with the instancing transform.
- modelMatrix = Matrix4.clone(
- sceneGraph.computedModelMatrix,
- modelMatrix
- );
- modelMatrix = Matrix4.multiplyTransformation(
- modelMatrix,
- runtimeNode.computedTransform,
- modelMatrix
- );
-
- nodeComputedTransform = Matrix4.clone(
- Matrix4.IDENTITY,
- nodeComputedTransform
- );
}
}
- let computedModelMatrix = Matrix4.multiplyTransformation(
- modelMatrix,
- nodeComputedTransform,
- scratchModelMatrix
- );
if (frameState.mode !== SceneMode.SCENE3D) {
- computedModelMatrix = Transforms.basisTo2D(
+ modelMatrix = Transforms.basisTo2D(
frameState.mapProjection,
- computedModelMatrix,
- computedModelMatrix
+ modelMatrix,
+ modelMatrix
);
}
+ const computedModelMatrix = Matrix4.multiplyTransformation(
+ modelMatrix,
+ nodeComputedTransform,
+ scratchcomputedModelMatrix
+ );
+
const transforms = [];
if (defined(instances)) {
const transformsCount = instances.attributes[0].count;
@@ -130,26 +117,70 @@ export default function pickModel(
if (defined(transformsTypedArray)) {
for (let i = 0; i < transformsCount; i++) {
- const transform = Matrix4.unpack(
- transformsTypedArray,
- i * transformElements,
- scratchInstanceMatrix
+ const index = i * transformElements;
+
+ const transform = new Matrix4(
+ transformsTypedArray[index],
+ transformsTypedArray[index + 1],
+ transformsTypedArray[index + 2],
+ transformsTypedArray[index + 3],
+ transformsTypedArray[index + 4],
+ transformsTypedArray[index + 5],
+ transformsTypedArray[index + 6],
+ transformsTypedArray[index + 7],
+ transformsTypedArray[index + 8],
+ transformsTypedArray[index + 9],
+ transformsTypedArray[index + 10],
+ transformsTypedArray[index + 11],
+ 0,
+ 0,
+ 0,
+ 1
);
- transform[12] = 0.0;
- transform[13] = 0.0;
- transform[14] = 0.0;
- transform[15] = 1.0;
+
+ if (instances.transformInWorldSpace) {
+ Matrix4.multiplyTransformation(
+ transform,
+ nodeComputedTransform,
+ transform
+ );
+ Matrix4.multiplyTransformation(modelMatrix, transform, transform);
+ } else {
+ Matrix4.multiplyTransformation(
+ transform,
+ computedModelMatrix,
+ transform
+ );
+ }
transforms.push(transform);
}
}
}
if (transforms.length === 0) {
- transforms.push(Matrix4.IDENTITY);
+ transforms.push(computedModelMatrix);
}
- for (let j = 0; j < node.primitives.length; j++) {
- const primitive = node.primitives[j];
+ const primitivesLength = runtimeNode.runtimePrimitives.length;
+ for (let j = 0; j < primitivesLength; j++) {
+ const runtimePrimitive = runtimeNode.runtimePrimitives[j];
+ const primitive = runtimePrimitive.primitive;
+
+ if (defined(runtimePrimitive.boundingSphere) && !defined(instances)) {
+ const boundingSphere = BoundingSphere.transform(
+ runtimePrimitive.boundingSphere,
+ computedModelMatrix,
+ scratchBoundingSphere
+ );
+ const boundsIntersection = IntersectionTests.raySphere(
+ ray,
+ boundingSphere
+ );
+ if (!defined(boundsIntersection)) {
+ continue;
+ }
+ }
+
const positionAttribute = ModelUtility.getAttributeBySemantic(
primitive,
VertexAttributeSemantic.POSITION
@@ -165,12 +196,17 @@ export default function pickModel(
if (!defined(indices)) {
const indicesBuffer = primitive.indices.buffer;
const indicesCount = primitive.indices.count;
+ const indexDatatype = primitive.indices.indexDatatype;
if (defined(indicesBuffer) && frameState.context.webgl2) {
- const useUint8Array = indicesBuffer.sizeInBytes === indicesCount;
- indices = useUint8Array
- ? new Uint8Array(indicesCount)
- : IndexDatatype.createTypedArray(vertexCount, indicesCount);
- indicesBuffer.getBufferData(indices, 0, 0, indicesCount);
+ if (indexDatatype === IndexDatatype.UNSIGNED_BYTE) {
+ indices = new Uint8Array(indicesCount);
+ } else if (indexDatatype === IndexDatatype.UNSIGNED_SHORT) {
+ indices = new Uint16Array(indicesCount);
+ } else if (indexDatatype === IndexDatatype.UNSIGNED_INT) {
+ indices = new Uint32Array(indicesCount);
+ }
+
+ indicesBuffer.getBufferData(indices);
}
}
@@ -230,7 +266,6 @@ export default function pickModel(
numComponents,
quantization,
instanceTransform,
- computedModelMatrix,
scratchV0
);
const v1 = getVertexPosition(
@@ -239,7 +274,6 @@ export default function pickModel(
numComponents,
quantization,
instanceTransform,
- computedModelMatrix,
scratchV1
);
const v2 = getVertexPosition(
@@ -248,7 +282,6 @@ export default function pickModel(
numComponents,
quantization,
instanceTransform,
- computedModelMatrix,
scratchV2
);
@@ -257,7 +290,7 @@ export default function pickModel(
v0,
v1,
v2,
- defaultValue(cullBackFaces, true)
+ defaultValue(model.backFaceCulling, true)
);
if (defined(t)) {
@@ -281,8 +314,8 @@ export default function pickModel(
const projection = frameState.mapProjection;
const ellipsoid = projection.ellipsoid;
- const cart = projection.unproject(result, scratchPickCartographic);
- ellipsoid.cartographicToCartesian(cart, result);
+ const cartographic = projection.unproject(result, scratchPickCartographic);
+ ellipsoid.cartographicToCartesian(cartographic, result);
}
return result;
@@ -294,7 +327,6 @@ function getVertexPosition(
numComponents,
quantization,
instanceTransform,
- computedModelMatrix,
result
) {
const i = index * numComponents;
@@ -332,7 +364,6 @@ function getVertexPosition(
}
result = Matrix4.multiplyByPoint(instanceTransform, result, result);
- result = Matrix4.multiplyByPoint(computedModelMatrix, result, result);
return result;
}
diff --git a/packages/engine/Source/Scene/Multiple3DTileContent.js b/packages/engine/Source/Scene/Multiple3DTileContent.js
index bef1cb3eb6cb..42ede698a349 100644
--- a/packages/engine/Source/Scene/Multiple3DTileContent.js
+++ b/packages/engine/Source/Scene/Multiple3DTileContent.js
@@ -656,18 +656,12 @@ Multiple3DTileContent.prototype.update = function (tileset, frameState) {
*
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
- * @param {boolean} [cullBackFaces=true] If false, back faces are not culled and will return an intersection if picked.
* @param {Cartesian3|undefined} [result] The intersection or undefined
if none was found.
* @returns {Cartesian3|undefined} The intersection or undefined
if none was found.
*
* @private
*/
-Multiple3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Multiple3DTileContent.prototype.pick = function (ray, frameState, result) {
if (!this._ready) {
return undefined;
}
@@ -678,7 +672,7 @@ Multiple3DTileContent.prototype.pick = function (
const length = contents.length;
for (let i = 0; i < length; ++i) {
- const candidate = contents[i].pick(ray, frameState, cullBackFaces, result);
+ const candidate = contents[i].pick(ray, frameState, result);
if (!defined(candidate)) {
continue;
diff --git a/packages/engine/Source/Scene/Tileset3DTileContent.js b/packages/engine/Source/Scene/Tileset3DTileContent.js
index 655f0e675ef1..41d1652453c1 100644
--- a/packages/engine/Source/Scene/Tileset3DTileContent.js
+++ b/packages/engine/Source/Scene/Tileset3DTileContent.js
@@ -168,6 +168,10 @@ Tileset3DTileContent.prototype.applyStyle = function (style) {};
Tileset3DTileContent.prototype.update = function (tileset, frameState) {};
+Tileset3DTileContent.prototype.pick = function (ray, frameState, result) {
+ return undefined;
+};
+
Tileset3DTileContent.prototype.isDestroyed = function () {
return false;
};
diff --git a/packages/engine/Source/Scene/Vector3DTileContent.js b/packages/engine/Source/Scene/Vector3DTileContent.js
index 4d779bbf562b..120a22db5801 100644
--- a/packages/engine/Source/Scene/Vector3DTileContent.js
+++ b/packages/engine/Source/Scene/Vector3DTileContent.js
@@ -727,12 +727,7 @@ Vector3DTileContent.prototype.update = function (tileset, frameState) {
}
};
-Vector3DTileContent.prototype.pick = function (
- ray,
- frameState,
- cullBackFaces,
- result
-) {
+Vector3DTileContent.prototype.pick = function (ray, frameState, result) {
return undefined;
};
diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
index 2e3381f89d49..6b85a670e228 100644
--- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
+++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js
@@ -2414,6 +2414,151 @@ describe(
});
});
+ it("picks", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(scene, tilesetUrl);
+ viewRootOnly();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215026.8094312553,
+ -4736367.339076743,
+ 4081652.238842398
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks tileset of tilesets", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ tilesetOfTilesetsUrl
+ );
+ viewRootOnly();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215026.8094312553,
+ -4736367.339076743,
+ 4081652.238842398
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks instanced tileset", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ instancedUrl
+ );
+ viewInstances();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215015.7820120894,
+ -4736324.352446682,
+ 4081615.004915994
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks translucent tileset", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ translucentUrl
+ );
+ viewAllTiles();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215013.1035421563,
+ -4736313.911345786,
+ 4081605.96109977
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picks tileset with transforms", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ tilesetWithTransformsUrl
+ );
+ viewAllTiles();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(
+ 1215013.8353220497,
+ -4736316.763939952,
+ 4081608.4319443353
+ );
+ expect(tileset.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
+ it("picking point cloud tileset returns undefined", async function () {
+ const tileset = await Cesium3DTilesTester.loadTileset(
+ scene,
+ pointCloudUrl
+ );
+ viewAllTiles();
+ scene.renderForSpecs();
+
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ expect(tileset.pick(ray, scene.frameState)).toBeUndefined();
+ });
+
it("destroys", function () {
return Cesium3DTilesTester.loadTileset(scene, tilesetUrl).then(function (
tileset
diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js
index df62257eb949..a93f00370367 100644
--- a/packages/engine/Specs/Scene/Model/ModelSpec.js
+++ b/packages/engine/Specs/Scene/Model/ModelSpec.js
@@ -4392,6 +4392,28 @@ describe(
});
});
+ it("pick returns position of intersection between ray and model surface", async function () {
+ const model = await loadAndZoomToModelAsync(
+ {
+ url: boxTexturedGltfUrl,
+ enablePick: !scene.frameState.context.webgl2,
+ },
+ scene
+ );
+ const ray = scene.camera.getPickRay(
+ new Cartesian2(
+ scene.drawingBufferWidth / 2.0,
+ scene.drawingBufferHeight / 2.0
+ )
+ );
+
+ const expected = new Cartesian3(0.5, 0, 0.5);
+ expect(model.pick(ray, scene.frameState)).toEqualEpsilon(
+ expected,
+ CesiumMath.EPSILON12
+ );
+ });
+
it("destroy works", function () {
spyOn(ShaderProgram.prototype, "destroy").and.callThrough();
return loadAndZoomToModelAsync({ gltf: boxTexturedGlbUrl }, scene).then(
diff --git a/packages/engine/Specs/Scene/Model/pickModelSpec.js b/packages/engine/Specs/Scene/Model/pickModelSpec.js
index 7aea7e85903d..a3a0d0ac4a2c 100644
--- a/packages/engine/Specs/Scene/Model/pickModelSpec.js
+++ b/packages/engine/Specs/Scene/Model/pickModelSpec.js
@@ -2,6 +2,7 @@ import {
pickModel,
Cartesian2,
Cartesian3,
+ HeadingPitchRange,
Math as CesiumMath,
Model,
Ray,
@@ -15,7 +16,7 @@ describe("Scene/Model/pickModel", function () {
const boxTexturedGltfUrl =
"./Data/Models/glTF-2.0/BoxTextured/glTF/BoxTextured.gltf";
const boxInstanced =
- "./Data/Models/glTF-2.0/BoxInstanced/glTF/box-instanced.gltf";
+ "./Data/Models/glTF-2.0/BoxInstancedNoNormals/glTF/BoxInstancedNoNormals.gltf";
const boxWithOffsetUrl =
"./Data/Models/glTF-2.0/BoxWithOffset/glTF/BoxWithOffset.gltf";
const pointCloudUrl =
@@ -26,6 +27,8 @@ describe("Scene/Model/pickModel", function () {
"./Data/Models/glTF-2.0/BoxWeb3dQuantizedAttributes/glTF/BoxWeb3dQuantizedAttributes.gltf";
const boxCesiumRtcUrl =
"./Data/Models/glTF-2.0/BoxCesiumRtc/glTF/BoxCesiumRtc.gltf";
+ const boxBackFaceCullingUrl =
+ "./Data/Models/glTF-2.0/BoxBackFaceCulling/glTF/BoxBackFaceCulling.gltf";
let scene;
beforeAll(function () {
@@ -223,10 +226,19 @@ describe("Scene/Model/pickModel", function () {
});
it("returns position of intersection with instanced model", async function () {
+ // None of the 4 instanced cubes are in the center of the model's bounding
+ // sphere, so set up a camera view that focuses in on one of them.
+ const offset = new HeadingPitchRange(
+ CesiumMath.PI_OVER_TWO,
+ -CesiumMath.PI_OVER_FOUR,
+ 1
+ );
+
const model = await loadAndZoomToModelAsync(
{
url: boxInstanced,
enablePick: !scene.frameState.context.webgl2,
+ offset,
},
scene
);
@@ -237,7 +249,7 @@ describe("Scene/Model/pickModel", function () {
)
);
- const expected = new Cartesian3(0.278338500214, 0, 0.278338500214);
+ const expected = new Cartesian3(0, -0.5, 0.5);
expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
expected,
CesiumMath.EPSILON12
@@ -281,11 +293,12 @@ describe("Scene/Model/pickModel", function () {
expect(pickModel(model, ray, scene.frameState)).toBeUndefined();
});
- it("includes back faces results when cullsBackFaces is false", async function () {
+ it("includes back faces results when model disbales backface culling", async function () {
const model = await loadAndZoomToModelAsync(
{
- url: boxTexturedGltfUrl,
+ url: boxBackFaceCullingUrl,
enablePick: !scene.frameState.context.webgl2,
+ backFaceCulling: false,
},
scene
);
@@ -295,10 +308,15 @@ describe("Scene/Model/pickModel", function () {
scene.drawingBufferHeight / 2.0
)
);
+
ray.origin = model.boundingSphere.center;
- const expected = new Cartesian3(-0.5, 0, -0.5);
- expect(pickModel(model, ray, scene.frameState, false)).toEqualEpsilon(
+ const expected = new Cartesian3(
+ -0.9999998807907355,
+ 0,
+ -0.9999998807907104
+ );
+ expect(pickModel(model, ray, scene.frameState)).toEqualEpsilon(
expected,
CesiumMath.EPSILON12
);