@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
221 lines (197 loc) • 7.13 kB
JavaScript
import BoundingSphere from "../../Core/BoundingSphere.js";
import Cartesian3 from "../../Core/Cartesian3.js";
import Cartographic from "../../Core/Cartographic.js";
import Check from "../../Core/Check.js";
import defined from "../../Core/defined.js";
import Ellipsoid from "../../Core/Ellipsoid.js";
import IntersectionTests from "../../Core/IntersectionTests.js";
import Matrix4 from "../../Core/Matrix4.js";
import Ray from "../../Core/Ray.js";
import VerticalExaggeration from "../../Core/VerticalExaggeration.js";
import AttributeType from "../AttributeType.js";
import SceneMode from "../SceneMode.js";
import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
import ModelReader from "./ModelReader.js";
import ModelUtility from "./ModelUtility.js";
const scratchV0 = new Cartesian3();
const scratchV1 = new Cartesian3();
const scratchV2 = new Cartesian3();
const scratchV0_t = new Cartesian3();
const scratchV1_t = new Cartesian3();
const scratchV2_t = new Cartesian3();
const scratchPickCartographic = new Cartographic();
const scratchBoundingSphere = new BoundingSphere();
/**
* Reads a 3D position from a flat vertex array at the given element index.
*
* @param {TypedArray} vertices The flat array of vertex components.
* @param {number} index The vertex index (element index, not byte offset).
* @param {number} elementStride The number of components per vertex element.
* @param {Cartesian3} result The object into which to store the position.
* @returns {Cartesian3} The modified result parameter.
* @private
*/
function readPosition(vertices, index, elementStride, result) {
const i = index * elementStride;
result.x = vertices[i];
result.y = vertices[i + 1];
result.z = vertices[i + 2];
return result;
}
/**
* Find an intersection between a ray and the model surface that was rendered. The ray must be given in world coordinates.
*
* @param {Model} model The model to pick.
* @param {Ray} ray The ray to test for intersection.
* @param {FrameState} frameState The frame state.
* @param {number} [verticalExaggeration=1.0] A scalar used to exaggerate the height of a position relative to the ellipsoid. If the value is 1.0 there will be no effect.
* @param {number} [relativeHeight=0.0] The ellipsoid height relative to which a position is exaggerated. If the value is 0.0 the position will be exaggerated relative to the ellipsoid surface.
* @param {Ellipsoid} [ellipsoid=Ellipsoid.default] The ellipsoid to which the exaggerated position is relative.
* @param {Cartesian3|undefined} [result] The intersection or <code>undefined</code> if none was found.
* @returns {Cartesian3|undefined} The intersection or <code>undefined</code> if none was found.
*
* @private
*/
export default function pickModel(
model,
ray,
frameState,
verticalExaggeration,
relativeHeight,
ellipsoid,
result,
) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("model", model);
Check.typeOf.object("ray", ray);
Check.typeOf.object("frameState", frameState);
//>>includeEnd('debug');
if (!model._ready || frameState.mode === SceneMode.MORPHING) {
return;
}
let minT = Number.MAX_VALUE;
ellipsoid = ellipsoid ?? Ellipsoid.default;
verticalExaggeration = verticalExaggeration ?? 1.0;
relativeHeight = relativeHeight ?? 0.0;
const mapProjection =
frameState.mode !== SceneMode.SCENE3D
? frameState.mapProjection
: undefined;
ModelReader.forEachPrimitive(
model,
{ mapProjection: mapProjection },
function (runtimePrimitive, primitive, instances, computedModelMatrix) {
// Bounding sphere early-out for non-instanced primitives
if (defined(runtimePrimitive.boundingSphere) && instances.length === 1) {
const boundingSphere = BoundingSphere.transform(
runtimePrimitive.boundingSphere,
computedModelMatrix,
scratchBoundingSphere,
);
const boundsIntersection = IntersectionTests.raySphere(
ray,
boundingSphere,
);
if (!defined(boundsIntersection)) {
return;
}
}
if (!defined(primitive.indices)) {
// Point clouds
return;
}
let vertices;
let numPosComponents;
const positionAttribute = ModelUtility.getAttributeBySemantic(
primitive,
VertexAttributeSemantic.POSITION,
);
if (defined(positionAttribute)) {
numPosComponents = AttributeType.getNumberOfComponents(
positionAttribute.type,
);
vertices = ModelReader.readAttributeAsTypedArray(positionAttribute);
}
let indices;
if (defined(primitive.indices)) {
indices = ModelReader.readIndicesAsTypedArray(primitive.indices);
}
if (!defined(indices) || !defined(vertices)) {
return;
}
const indicesLength = indices.length;
for (let i = 0; i < indicesLength; i += 3) {
const i0 = indices[i];
const i1 = indices[i + 1];
const i2 = indices[i + 2];
readPosition(vertices, i0, numPosComponents, scratchV0);
readPosition(vertices, i1, numPosComponents, scratchV1);
readPosition(vertices, i2, numPosComponents, scratchV2);
for (const instance of instances) {
const v0 = Matrix4.multiplyByPoint(
instance.transform,
scratchV0,
scratchV0_t,
);
const v1 = Matrix4.multiplyByPoint(
instance.transform,
scratchV1,
scratchV1_t,
);
const v2 = Matrix4.multiplyByPoint(
instance.transform,
scratchV2,
scratchV2_t,
);
if (verticalExaggeration !== 1.0) {
VerticalExaggeration.getPosition(
v0,
ellipsoid,
verticalExaggeration,
relativeHeight,
v0,
);
VerticalExaggeration.getPosition(
v1,
ellipsoid,
verticalExaggeration,
relativeHeight,
v1,
);
VerticalExaggeration.getPosition(
v2,
ellipsoid,
verticalExaggeration,
relativeHeight,
v2,
);
}
const t = IntersectionTests.rayTriangleParametric(
ray,
v0,
v1,
v2,
model.backFaceCulling ?? true,
);
if (defined(t)) {
if (t < minT && t >= 0.0) {
minT = t;
}
}
}
}
},
);
if (minT === Number.MAX_VALUE) {
return undefined;
}
result = Ray.getPoint(ray, minT, result);
if (frameState.mode !== SceneMode.SCENE3D) {
Cartesian3.fromElements(result.y, result.z, result.x, result);
const projection = frameState.mapProjection;
const ellipsoid = projection.ellipsoid;
const cartographic = projection.unproject(result, scratchPickCartographic);
ellipsoid.cartographicToCartesian(cartographic, result);
}
return result;
}