// @ts-check
import assert from "../Core/assert.js";
/** @import BufferPrimitiveCollection from './BufferPrimitiveCollection.js'; */
/** @import BufferPrimitiveMaterial from "./BufferPrimitiveMaterial.js"; */
/**
* View bound to the underlying buffer data of a {@link BufferPrimitiveCollection}. Abstract.
*
*
BufferPrimitive instances are intended to be reused when iterating over large collections,
* and temporarily bound to a primitive index while performing read/write operations on that primitive,
* before being rebound to the next primitive, using the
* {@link https://en.wikipedia.org/wiki/Flyweight_pattern|flyweight pattern}.
*
* @see BufferPrimitiveCollection
* @see BufferPrimitiveMaterial
* @see BufferPoint
* @see BufferPolyline
* @see BufferPolygon
*
* @abstract
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*/
class BufferPrimitive {
/**
* Collecton containing the primitive(s) for which this instance currently
* provides a view.
*
* @type {BufferPrimitiveCollection}
* @ignore
*/
_collection = null;
/**
* Index of the primitive for which this instance currently provides a view.
*
* @type {number}
* @ignore
*/
_index = -1;
/**
* Byte offset, into the collection's primitive buffer, of the primitive for
* which this instance currently provides a view.
*
* @type {number}
* @ignore
*/
_byteOffset = -1;
/**
* Binary layout for this primitive type in collection's primitive buffer.
* Each `Layout.MY_KEY_U32` entry is an offset in bytes, relative to the
* start offset of the primitive, with the suffix indicating the data type
* stored at that offset.
*
* The final entry, `__BYTE_LENGTH`, is not a pointer into a buffer — its
* literal value is the total byte length of one primitive in the primitive
* buffer, exclusive of other buffers.
*
* @ignore
*/
static Layout = {
/**
* Feature ID associated with the primitive; not required to be unique.
* @type {number}
* @ignore
*/
FEATURE_ID_U32: 0,
/**
* Boolean (0 or 1) flag indicating whether primitive is shown.
* @type {number}
* @ignore
*/
SHOW_U8: 4,
/**
* Boolean (0 or 1) flag indicating whether primitive is dirty.
* @type {number}
* @ignore
*/
DIRTY_U8: 5,
/**
* Pick ID (uint32) of primitive.
* @type {number}
* @ignore
*/
PICK_ID_U32: 8,
/**
* Byte length of one primitive in the primitive buffer, exclusive of
* other buffers. Literal value, not a pointer.
* @type {number}
* @ignore
*/
__BYTE_LENGTH: 12,
};
/////////////////////////////////////////////////////////////////////////////
// LIFECYCLE
/**
* Copies data from source primitive to result. If the result primitive is not
* new (the last primitive in the collection) then source and result primitives
* must have the same vertex counts.
*
* @param {BufferPrimitive} primitive
* @param {BufferPrimitive} result
* @returns {BufferPrimitive}
*/
static clone(primitive, result) {
const SrcMaterialClass = primitive._collection._getMaterialClass();
//>>includeStart('debug', pragmas.debug);
const DstMaterialClass = result._collection._getMaterialClass();
assert(SrcMaterialClass === DstMaterialClass, "Incompatible materials");
//>>includeEnd('debug');
result.featureId = primitive.featureId;
result.show = primitive.show;
result.setMaterial(primitive.getMaterial(new SrcMaterialClass()));
return result;
}
/**
* Returns true if this primitive's memory footprint is resizable. Only the
* newest (most recently created) primitive in a collection can be resized,
* to guarantee fast and stable performance.
*
* @returns {boolean}
* @protected
* @ignore
*/
_isResizable() {
return this._index === this._collection.primitiveCount - 1;
}
/////////////////////////////////////////////////////////////////////////////
// ACCESSORS
/**
* Feature ID associated with the primitive; not required to be unique.
* @type {number}
*/
get featureId() {
return this._getUint32(BufferPrimitive.Layout.FEATURE_ID_U32);
}
set featureId(featureId) {
this._setUint32(BufferPrimitive.Layout.FEATURE_ID_U32, featureId);
}
/**
* Whether primitive is shown.
* @type {boolean}
*/
get show() {
return this._getUint8(BufferPrimitive.Layout.SHOW_U8) === 1;
}
set show(show) {
this._setUint8(BufferPrimitive.Layout.SHOW_U8, show ? 1 : 0);
}
/**
* @param {BufferPrimitiveMaterial} result
* @returns {BufferPrimitiveMaterial}
*/
getMaterial(result) {
const collection = this._collection;
const MaterialClass = collection._getMaterialClass();
return MaterialClass.unpack(
collection._materialView,
this._index * MaterialClass.packedLength,
result,
);
}
/**
* @param {BufferPrimitiveMaterial} material
*/
setMaterial(material) {
const collection = this._collection;
const MaterialClass = collection._getMaterialClass();
MaterialClass.pack(
material,
collection._materialView,
this._index * MaterialClass.packedLength,
);
this._dirty = true;
return material;
}
/**
* Whether the primitive requires an update on next render. Renderers should
* _not_ iterate over all primitives each frame, but must instead inspect
* only the dirty range of the parent collection. This flag is managed
* automatically, by primitive setters and collection renderers.
*
* @type {boolean}
* @ignore
*
* @see BufferPrimitiveCollection#_dirtyOffset
* @see BufferPrimitiveCollection#_dirtyCount
*/
get _dirty() {
return this._getUint8(BufferPrimitive.Layout.DIRTY_U8) === 1;
}
set _dirty(dirty) {
// Avoid `._setUint8()` here, which would infinitely loop `._dirty = true`.
this._collection._primitiveView.setUint8(
this._byteOffset + BufferPrimitive.Layout.DIRTY_U8,
dirty ? 1 : 0,
);
// A 'dirty' primitive is responsible for notifying the collection. Applying
// updates and marking the primitive 'clean' will be handled by the collection,
// so we don't notify the collection here in that case.
if (dirty) {
this._collection._makeDirty(this._index);
}
}
/**
* Pick ID (uint32) of primitive.
* @type {number}
* @ignore
*/
get _pickId() {
return this._getUint32(BufferPrimitive.Layout.PICK_ID_U32);
}
set _pickId(pickId) {
this._setUint32(BufferPrimitive.Layout.PICK_ID_U32, pickId);
}
/////////////////////////////////////////////////////////////////////////////
// BUFFER ACCESSORS
/**
* @param {number} itemByteOffset
* @returns {number}
* @ignore
*/
_getUint8(itemByteOffset) {
return this._collection._primitiveView.getUint8(
this._byteOffset + itemByteOffset,
);
}
/**
* @param {number} itemByteOffset
* @param {number} itemValue
* @ignore
*/
_setUint8(itemByteOffset, itemValue) {
this._collection._primitiveView.setUint8(
this._byteOffset + itemByteOffset,
itemValue,
);
this._dirty = true;
}
/**
* @param {number} itemByteOffset
* @returns {number}
* @ignore
*/
_getUint32(itemByteOffset) {
return this._collection._primitiveView.getUint32(
this._byteOffset + itemByteOffset,
true,
);
}
/**
* @param {number} itemByteOffset
* @param {number} itemValue
* @ignore
*/
_setUint32(itemByteOffset, itemValue) {
this._collection._primitiveView.setUint32(
this._byteOffset + itemByteOffset,
itemValue,
true,
);
this._dirty = true;
}
/**
* @param {number} itemByteOffset
* @returns {number}
* @ignore
*/
_getFloat32(itemByteOffset) {
return this._collection._primitiveView.getFloat32(
this._byteOffset + itemByteOffset,
true,
);
}
/**
* @param {number} itemByteOffset
* @param {number} itemValue
* @ignore
*/
_setFloat32(itemByteOffset, itemValue) {
this._collection._primitiveView.setFloat32(
this._byteOffset + itemByteOffset,
itemValue,
true,
);
this._dirty = true;
}
/////////////////////////////////////////////////////////////////////////////
// DEBUG
/**
* Returns a JSON-serializable object representing the primitive. This encoding
* is not memory-efficient, and should generally be used for debugging and
* testing.
*
* @returns {Object} JSON-serializable object.
*/
toJSON() {
const collection = this._collection;
const MaterialClass = collection._getMaterialClass();
return {
featureId: this.featureId,
show: this.show,
dirty: this._dirty,
material: this.getMaterial(new MaterialClass()).toJSON(),
};
}
}
export default BufferPrimitive;