几何体旋转都是以几何原点为基准旋转的 cesium提供的圆锥体几何原点是几何体中心不是圆锥顶点,这里可以使用primitive-geometry重新构建出以顶点为原点的几何体进行绘制 然后进行旋转变换;
提供一个我仿照cesium圆锥构建的类
`
import { BoundingSphere, Cartesian2, Cartesian3, Math as CesiumMath, defaultValue, defined, DeveloperError, Geometry, Primitive, PrimitiveType, VertexFormat, GeometryAttribute, ComponentDatatype, IndexDatatype, GeometryAttributes } from "cesium";
function arrayFill(array: any[] | Uint8Array, value: any, start?: number, end?: number) {
if (typeof array.fill === "function") {
return array.fill(value, start, end);
}
const length = array.length >>> 0;
const relativeStart = defaultValue(start, 0);
// If negative, find wrap around position
let k =
relativeStart < 0
? Math.max(length + relativeStart, 0)
: Math.min(relativeStart, length);
const relativeEnd = defaultValue(end, length);
// If negative, find wrap around position
const last =
relativeEnd < 0
? Math.max(length + relativeEnd, 0)
: Math.min(relativeEnd, length);
// Fill array accordingly
while (k < last) {
array[k] = value;
k++;
}
return array;
}
function computePositions(length: number, topRadius: number, bottomRadius: number, slices: number, fill: boolean) {
const topZ = 0;
const bottomZ = -length;
const twoSlice = slices + slices;
const size = fill ? 2 * twoSlice : twoSlice;
const positions = new Float64Array(size * 3);
let i;
let index = 0;
let tbIndex = 0;
const bottomOffset = fill ? twoSlice * 3 : 0;
const topOffset = fill ? (twoSlice + slices) * 3 : slices * 3;
for (i = 0; i < slices; i++) {
const angle = (i / slices) * CesiumMath.TWO_PI;
const x = Math.cos(angle);
const y = Math.sin(angle);
const bottomX = x * bottomRadius;
const bottomY = y * bottomRadius;
const topX = x * topRadius;
const topY = y * topRadius;
positions[tbIndex + bottomOffset] = bottomX;
positions[tbIndex + bottomOffset + 1] = bottomY;
positions[tbIndex + bottomOffset + 2] = bottomZ;
positions[tbIndex + topOffset] = topX;
positions[tbIndex + topOffset + 1] = topY;
positions[tbIndex + topOffset + 2] = topZ;
tbIndex += 3;
if (fill) {
positions[index++] = bottomX;
positions[index++] = bottomY;
positions[index++] = bottomZ;
positions[index++] = topX;
positions[index++] = topY;
positions[index++] = topZ;
}
}
return positions;
};
type ConeGeometryOption = {
length: number;
topRadius?: number;
bottomRadius: number;
slices?: number;
vertexFormat?: VertexFormat;
numberOfVerticalLines?: number;
offsetAttribute?: number;
}
function createGeometry(options: ConeGeometryOption) {
let length = options.length;
const topRadius = options.topRadius || 0;
const bottomRadius = options.bottomRadius;
const vertexFormat = options.vertexFormat as VertexFormat;
const slices = options.slices as number;
const offsetAttribute = options.offsetAttribute;
if (
length <= 0 ||
topRadius < 0 ||
bottomRadius < 0 ||
(topRadius === 0 && bottomRadius === 0)
) {
return;
}
const twoSlices = slices + slices;
const threeSlices = slices + twoSlices;
const numVertices = twoSlices + twoSlices;
const positions = computePositions(length, topRadius, bottomRadius, slices, true);
const st = vertexFormat.st ? new Float32Array(numVertices * 2) : undefined;
const normals = vertexFormat.normal ? new Float32Array(numVertices * 3) : undefined;
const tangents = vertexFormat.tangent ? new Float32Array(numVertices * 3) : undefined;
const bitangents = vertexFormat.bitangent ? new Float32Array(numVertices * 3) : undefined;
let i;
const computeNormal = vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent;
if (computeNormal) {
const computeTangent = vertexFormat.tangent || vertexFormat.bitangent;
let normalIndex = 0;
let tangentIndex = 0;
let bitangentIndex = 0;
const theta = Math.atan2(bottomRadius - topRadius, length);
const normal = new Cartesian3();
normal.z = Math.sin(theta);
const normalScale = Math.cos(theta);
let tangent = new Cartesian3();
let bitangent = new Cartesian3();
for (i = 0; i < slices; i++) {
const angle = (i / slices) * CesiumMath.TWO_PI;
const x = normalScale * Math.cos(angle);
const y = normalScale * Math.sin(angle);
if (computeNormal) {
normal.x = x;
normal.y = y;
if (computeTangent) {
tangent = Cartesian3.normalize(
Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent),
tangent
);
}
if (normals) {
normals[normalIndex++] = normal.x;
normals[normalIndex++] = normal.y;
normals[normalIndex++] = normal.z;
normals[normalIndex++] = normal.x;
normals[normalIndex++] = normal.y;
normals[normalIndex++] = normal.z;
}
if (tangents) {
tangents[tangentIndex++] = tangent.x;
tangents[tangentIndex++] = tangent.y;
tangents[tangentIndex++] = tangent.z;
tangents[tangentIndex++] = tangent.x;
tangents[tangentIndex++] = tangent.y;
tangents[tangentIndex++] = tangent.z;
}
if (bitangents) {
bitangent = Cartesian3.normalize(
Cartesian3.cross(normal, tangent, bitangent),
bitangent
);
bitangents[bitangentIndex++] = bitangent.x;
bitangents[bitangentIndex++] = bitangent.y;
bitangents[bitangentIndex++] = bitangent.z;
bitangents[bitangentIndex++] = bitangent.x;
bitangents[bitangentIndex++] = bitangent.y;
bitangents[bitangentIndex++] = bitangent.z;
}
}
}
for (i = 0; i < slices; i++) {
if (normals) {
normals[normalIndex++] = 0;
normals[normalIndex++] = 0;
normals[normalIndex++] = -1;
}
if (tangents) {
tangents[tangentIndex++] = 1;
tangents[tangentIndex++] = 0;
tangents[tangentIndex++] = 0;
}
if (bitangents) {
bitangents[bitangentIndex++] = 0;
bitangents[bitangentIndex++] = -1;
bitangents[bitangentIndex++] = 0;
}
}
for (i = 0; i < slices; i++) {
if (normals) {
normals[normalIndex++] = 0;
normals[normalIndex++] = 0;
normals[normalIndex++] = 1;
}
if (tangents) {
tangents[tangentIndex++] = 1;
tangents[tangentIndex++] = 0;
tangents[tangentIndex++] = 0;
}
if (bitangents) {
bitangents[bitangentIndex++] = 0;
bitangents[bitangentIndex++] = 1;
bitangents[bitangentIndex++] = 0;
}
}
}
const numIndices = 12 * slices - 12;
// @ts-ignore
const indices = IndexDatatype.createTypedArray(numVertices, numIndices);
let index = 0;
let j = 0;
for (i = 0; i < slices - 1; i++) {
indices[index++] = j;
indices[index++] = j + 2;
indices[index++] = j + 3;
indices[index++] = j;
indices[index++] = j + 3;
indices[index++] = j + 1;
j += 2;
}
indices[index++] = twoSlices - 2;
indices[index++] = 0;
indices[index++] = 1;
indices[index++] = twoSlices - 2;
indices[index++] = 1;
indices[index++] = twoSlices - 1;
for (i = 1; i < slices - 1; i++) {
indices[index++] = twoSlices + i + 1;
indices[index++] = twoSlices + i;
indices[index++] = twoSlices;
}
for (i = 1; i < slices - 1; i++) {
indices[index++] = threeSlices;
indices[index++] = threeSlices + i;
indices[index++] = threeSlices + i + 1;
}
let textureCoordIndex = 0;
if (st) {
const rad = Math.max(topRadius, bottomRadius);
for (i = 0; i < numVertices; i++) {
// @ts-ignore
const position = Cartesian3.fromArray(positions, i * 3, new Cartesian3());
st[textureCoordIndex++] = (position.x + rad) / (2.0 * rad);
st[textureCoordIndex++] = (position.y + rad) / (2.0 * rad);
}
}
const attributes = new GeometryAttributes();
if (vertexFormat.position) {
attributes.position = new GeometryAttribute({
componentDatatype: ComponentDatatype.DOUBLE,
componentsPerAttribute: 3,
values: positions,
});
}
if (vertexFormat.normal) {
attributes.normal = new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: normals,
});
}
if (vertexFormat.tangent) {
attributes.tangent = new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: tangents,
});
}
if (vertexFormat.bitangent) {
attributes.bitangent = new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: bitangents,
});
}
if (vertexFormat.st) {
attributes.st = new GeometryAttribute({
componentDatatype: ComponentDatatype.FLOAT,
componentsPerAttribute: 2,
values: st,
});
}
const radiusScratch = new Cartesian2();
radiusScratch.x = length * 0.5;
radiusScratch.y = Math.max(bottomRadius, topRadius);
const boundingSphere = new BoundingSphere(
Cartesian3.ZERO,
Cartesian2.magnitude(radiusScratch)
);
if (defined(offsetAttribute)) {
length = positions.length;
const applyOffset = new Uint8Array(length / 3);
const offsetValue = offsetAttribute === 0 ? 0 : 1;
arrayFill(applyOffset, offsetValue);
// @ts-ignore
attributes.applyOffset = new GeometryAttribute({
componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
componentsPerAttribute: 1,
values: applyOffset,
});
}
return {
attributes: attributes,
indices: indices,
primitiveType: PrimitiveType.TRIANGLES,
boundingSphere: boundingSphere,
// @ts-ignore
offsetAttribute: offsetAttribute,
};
}
/**
- 圆锥-ptimiotive
- 以顶点为中心圆锥
- /
class ConeGeometry extends Geometry {
constructor(options: ConeGeometryOption) {
options.slices = defaultValue(options.slices, 128);
const vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT);
options.vertexFormat = VertexFormat.clone(vertexFormat);
if (defined(options.offsetAttribute) && options.offsetAttribute === 1) {throw new DeveloperError("GeometryOffsetAttribute.TOP is not a supported options.offsetAttribute for this geometry.");
}
options.offsetAttribute = options.offsetAttribute;
const geometryOption = createGeometry(options) as {attributes: GeometryAttributes;
primitiveType?: PrimitiveType;
indices?: Uint16Array | Uint32Array;
boundingSphere?: BoundingSphere;
};
super(geometryOption);
}
}
export default ConeGeometry;
viewer.scene.primitives.add(new Primitive({
geometryInstances: new GeometryInstance({
geometry: new ConeGeometry({
length: 10000,
bottomRadius: 2000,
})
}),
modelMatrix: modelMatrix,
asynchronous: false,
appearance: new MaterialAppearance({
closed: true,
material: Material.fromType('Color', {
color: Color.RED
})
}),
}))
});
`