Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions packages/engine/Source/Scene/Model/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ function Model(options) {
}
this._instanceFeatureIdLabel = instanceFeatureIdLabel;

this._runtimeInstancesLength = 0;
this._runtimeInstancesDirty = false;

this._featureTables = [];
this._featureTableId = undefined;
Expand Down Expand Up @@ -2260,21 +2260,28 @@ function updateVerticalExaggeration(model, frameState) {
}

function updateRuntimeModelInstances(model) {
if (
model.sceneGraph.modelInstances.length !== model._runtimeInstancesLength
) {
model.resetDrawCommands();
model._runtimeInstancesLength = model.sceneGraph.modelInstances.length;
}
let instance;
for (let i = 0; i < model.sceneGraph.modelInstances.length; i++) {
instance = model.sceneGraph.modelInstances.get(i);

if (instance._dirty) {
if (!model.sceneGraph.modelInstances._dirty) {
model.sceneGraph.modelInstances._dirty = true;
}
instance._dirty = false;
}

if (instance._drawDirty) {
if (!model._runtimeInstancesDirty) {
model._runtimeInstancesDirty = true;
}
instance._drawDirty = false;
}
}

if (model._runtimeInstancesDirty) {
model.resetDrawCommands();
model._runtimeInstancesDirty = false;
}
}

Expand Down
80 changes: 69 additions & 11 deletions packages/engine/Source/Scene/Model/ModelInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Matrix3 from "../../Core/Matrix3.js";
import Matrix4 from "../../Core/Matrix4.js";
import TranslationRotationScale from "../../Core/TranslationRotationScale.js";
import Quaternion from "../../Core/Quaternion.js";
import Color from "../../Core/Color.js";
import defined from "../../Core/defined.js";

const scratchTranslationRotationScale = new TranslationRotationScale();
const scratchRotation = new Matrix3();
Expand All @@ -21,7 +23,12 @@ class ModelInstance {
* Constructs a {@link ModelInstance}, a copy of a {@link Model} mesh, for efficiently rendering a large number of copies the same model using GPU mesh instancing.
* The position, orientation, and scale of the instance is determined by the specified {@link Matrix4}.
* @constructor
* @param {Matrix4} transform Matrix4 describing the transform of the instance
*
* @param {object} options Object with the following properties:
* @param {Matrix4} options.transform Matrix4 describing the transform of the instance.
* @param {boolean} [options.show=true] Determines if the instance in the collection will be shown.
* @param {Color} [options.color] A color that blends with the instance rendered color
*
* @example
* const position = Cesium.Cartesian3.fromDegrees(-75.1652, 39.9526);
*
Expand All @@ -36,21 +43,27 @@ class ModelInstance {
* Cesium.Ellipsoid.WGS84,
* fixedFrameTransform,
* );
* const modelInstance = new Cesium.ModelInstance(instanceModelMatrix);
* const modelInstance = new Cesium.ModelInstance({
* transform: instanceModelMatrix
* });
*/
constructor(transform) {
constructor(options) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("transform", transform);
Check.typeOf.object("options", options);
Check.typeOf.object("options.transform", options.transform);
//>>includeEnd('debug');

this._transform = transform;
this._transform = options.transform;
this._center = new Cartesian3();
this._relativeTransform = new Matrix4();
this._relativeScaledTransform = new Matrix4();
this._pickId = undefined;
this.show = options.show ?? true;
this.color = options.color;

this._updateTransform(transform);
this._updateTransform(options.transform);
this._dirty = false;
this._drawDirty = false;
}

/**
Expand Down Expand Up @@ -117,15 +130,60 @@ class ModelInstance {
return this._relativeScaledTransform;
}

/**
* The Pick Id of the instance.
* @type {string|undefined}
* @readonly
*/
get pickId() {
return this._pickId;
}

/**
* Whether or not to render the model instance.
*
* @type {boolean}
*
* @default true
*/
get show() {
return this._show;
}
set show(value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.bool("show", value);
//>>includeEnd('debug');

if (this._show === value) {
return;
}

this._show = value;
this._dirty = true;
}

/**
* The Color of the instance.
* @type {Color}
*/
get color() {
return this._color;
}
set color(value) {
//>>includeStart('debug', pragmas.debug);
if (defined(value)) {
Check.typeOf.object("color", value);
}
//>>includeEnd('debug');

if (
this._color === value ||
(defined(this._color) &&
defined(value) &&
Color.equals(this._color, value))
) {
return;
}

this._color = value;
this._drawDirty = true;
}

_updateTransform(transform) {
// Get center from the transform
this._center = Matrix4.getTranslation(transform, this._center);
Expand Down
52 changes: 38 additions & 14 deletions packages/engine/Source/Scene/Model/ModelInstanceCollection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Check from "../../Core/Check.js";
import defined from "../../Core/defined.js";
import DeveloperError from "../../Core/DeveloperError.js";
import ModelInstance from "./ModelInstance.js";
Expand Down Expand Up @@ -36,15 +35,19 @@ import RuntimeError from "../../Core/RuntimeError.js";
*
* // Add an instance at the specified transform to a collection
* const collection = new Cesium.ModelInstanceCollection();
* collection.add(instanceModelMatrix);
* collection.add({
* transform: instanceModelMatrix
* });
*
* // Add an instance to a model
* const model = await Cesium.Model.fromGltfAsync({
* url: "../../SampleData/models/GroundVehicle/GroundVehicle.glb",
* minimumPixelSize: 64,
* });
* viewer.scene.primitives.add(model);
* model.instances.add(instanceModelMatrix);
* model.instances.add({
* transform: instanceModelMatrix
* });
*/
function ModelInstanceCollection(options) {
this._instances = [];
Expand Down Expand Up @@ -75,17 +78,25 @@ ModelInstanceCollection.prototype.initialize = function (transforms) {
}

for (let i = 0; i < transforms.length; i++) {
const transform = transforms[i];
const instance = new ModelInstance(transform);
const instance = new ModelInstance({
transform: transforms[i],
});
this._instances.push(instance);
}

if (transforms.length > 0 && this._model !== undefined) {
this._model._runtimeInstancesDirty = true;
}
};

/**
* Creates and adds an instance with the specified transform to the collection.
* The added instance is returned so it can be modified or removed from the collection later.
*
* @param {Matrix4} transform A transform that represents an instance of a Model
* @param {object} options Object with the following properties:
* @param {Matrix4} options.transform Matrix4 describing the transform of the instance.
* @param {boolean} [options.show=true] Determines if the billboards in the collection will be shown.
* @param {Color} [options.color] Determines if the billboards in the collection will be shown.
* @returns {ModelInstance} The model instance that was added to the collection.
*
* @performance Calling <code>add</code> is expected constant time. However, the collection's vertex buffer
Expand All @@ -95,24 +106,27 @@ ModelInstanceCollection.prototype.initialize = function (transforms) {
* @example
* // Example: Provide a transform to add a model instance to the collection
* const collection = new ModelInstanceCollection()
* const instance = collection.add(transform)
* const instance = collection.add({
* transform: instanceTransform
* })
*
* @see ModelInstanceCollection#remove
* @see ModelInstanceCollection#removeAll
*/
ModelInstanceCollection.prototype.add = function (transform) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("transform", transform);
//>>includeEnd('debug');

ModelInstanceCollection.prototype.add = function (options) {
if (this._model?._loader._hasMeshGpuInstancing) {
throw new RuntimeError(
"Models with the EXT_mesh_gpu_instancing extension cannot use the ModelInstanceCollection class.",
);
}

const instance = new ModelInstance(transform, this);
const instance = new ModelInstance(options);
this._instances.push(instance);

if (this._model !== undefined) {
this._model._runtimeInstancesDirty = true;
}

return instance;
};

Expand All @@ -132,7 +146,9 @@ ModelInstanceCollection.prototype.add = function (transform) {
*
*
* @example
* const instance = collection.add(transform);
* const instance = collection.add({
* transform: transform
* });
* collection.remove(instance); // Returns true
*
* @see ModelInstanceCollection#add
Expand All @@ -152,6 +168,10 @@ ModelInstanceCollection.prototype.remove = function (instance) {

this._instances.splice(index, 1);

if (this._model !== undefined) {
this._model._runtimeInstancesDirty = true;
}

return true;
};

Expand All @@ -172,6 +192,10 @@ ModelInstanceCollection.prototype.remove = function (instance) {
ModelInstanceCollection.prototype.removeAll = function () {
const instances = this._instances;
instances.length = 0;

if (this._model !== undefined) {
this._model._runtimeInstancesDirty = true;
}
};

/**
Expand Down
10 changes: 7 additions & 3 deletions packages/engine/Source/Scene/Model/ModelInstancesUpdateStage.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ ModelInstancesUpdateStage.update = function (
*/
function updateRuntimeNode(runtimeNode, sceneGraph, frameState) {
const modelInstances = sceneGraph.modelInstances._instances;
const buffer = runtimeNode.instancingTransformsBuffer;

const showsTypedArray =
RuntimeModelInstancingPipelineStage._getShowsTypedArray(modelInstances);
runtimeNode.instanceShowsBuffer.copyFromArrayView(showsTypedArray);

const transformsTypedArray =
RuntimeModelInstancingPipelineStage._getTransformsTypedArray(
Expand All @@ -61,8 +64,9 @@ function updateRuntimeNode(runtimeNode, sceneGraph, frameState) {
runtimeNode.instancingTransformsBuffer.copyFromArrayView(
transformsTypedArray,
);

runtimeNode.instancingTransformsBuffer = buffer;
const colorsTypedArray =
RuntimeModelInstancingPipelineStage._getColorsTypedArray(modelInstances);
runtimeNode.instanceColorsBuffer.copyFromArrayView(colorsTypedArray);

const childrenLength = runtimeNode.children.length;

Expand Down
Loading