DSDSServer2024/build/build.20240805/static/js/Cesium-1.53/Specs/Scene/ModelInstanceCollectionSpec.js

693 lines
22 KiB
JavaScript

defineSuite([
'Scene/ModelInstanceCollection',
'Core/BoundingSphere',
'Core/Cartesian3',
'Core/defaultValue',
'Core/HeadingPitchRange',
'Core/HeadingPitchRoll',
'Core/JulianDate',
'Core/Matrix4',
'Core/PrimitiveType',
'Core/Resource',
'Core/Transforms',
'Scene/Model',
'Scene/ShadowMode',
'Specs/createScene',
'Specs/pollToPromise',
'ThirdParty/when'
], function(
ModelInstanceCollection,
BoundingSphere,
Cartesian3,
defaultValue,
HeadingPitchRange,
HeadingPitchRoll,
JulianDate,
Matrix4,
PrimitiveType,
Resource,
Transforms,
Model,
ShadowMode,
createScene,
pollToPromise,
when) {
'use strict';
var boxUrl = './Data/Models/Box/CesiumBoxTest.gltf';
var cesiumAirUrl = './Data/Models/CesiumAir/Cesium_Air.gltf';
var riggedFigureUrl = './Data/Models/rigged-figure-test/rigged-figure-test.gltf';
var movingBoxUrl = './Data/Models/moving-box/moving-box.gltf';
var scene;
var boxRadius;
beforeAll(function() {
scene = createScene();
return loadModel(boxUrl).then(function(model) {
boxRadius = model.boundingSphere.radius;
scene.primitives.remove(model);
});
});
beforeEach(function() {
scene.morphTo3D(0.0);
});
afterAll(function() {
scene.destroyForSpecs();
});
afterEach(function() {
scene.primitives.removeAll();
});
function loadModel(url) {
var model = scene.primitives.add(Model.fromGltf({
url : url
}));
return pollToPromise(function() {
// Render scene to progressively load the model
scene.renderForSpecs();
return model.ready;
}).then(function() {
return model;
});
}
function loadCollection(options) {
var collection = scene.primitives.add(new ModelInstanceCollection(options));
return pollToPromise(function() {
// Render scene to progressively load the model
scene.renderForSpecs();
return collection.ready;
}).then(function() {
zoomTo(collection, 0);
return collection;
});
}
function createInstances(count, heightOffset) {
heightOffset = defaultValue(heightOffset, 0.0);
var spacing = 20.0;
var centerLongitude = -123.0744619;
var centerLatitude = 44.0503706;
var height = 5000.0 + heightOffset;
var instances = [];
for (var i = 0; i < count; ++i) {
var instanceHeight = height + spacing * i;
var position = Cartesian3.fromDegrees(centerLongitude, centerLatitude, instanceHeight);
var heading = Math.PI/2.0;
var pitch = 0.0;
var roll = 0.0;
var hpr = new HeadingPitchRoll(heading, pitch, roll);
var modelMatrix = Transforms.headingPitchRollToFixedFrame(position, hpr);
instances.push({
modelMatrix : modelMatrix
});
}
return instances;
}
function getBoundingSphere(instances, modelRadius) {
var length = instances.length;
var points = new Array(length);
for (var i = 0; i < length; ++i) {
var translation = new Cartesian3();
Matrix4.getTranslation(instances[i].modelMatrix, translation);
points[i] = translation;
}
var boundingSphere = new BoundingSphere();
BoundingSphere.fromPoints(points, boundingSphere);
boundingSphere.radius += modelRadius;
return boundingSphere;
}
var centerScratch = new Cartesian3();
function zoomTo(collection, instance) {
var center = Matrix4.getTranslation(collection._instances[instance].modelMatrix, centerScratch);
var camera = scene.camera;
camera.lookAt(center, new HeadingPitchRange(0.0, 0.0, 10.0));
}
function expectRender(collection, expectColor, time) {
expectColor = defaultValue(expectColor, true);
collection.show = false;
expect(scene).toRender([0, 0, 0, 255]);
collection.show = true;
// Verify each instance
var length = collection.length;
for (var i = 0; i < length; ++i) {
zoomTo(collection, i);
if (expectColor) {
expect({
scene : scene,
time : time
}).notToRender([0, 0, 0, 255]);
} else {
expect({
scene : scene,
time : time
}).toRender([0, 0, 0, 255]);
}
}
}
function verifyPickedInstance(collection, instanceId) {
return function(result) {
expect(result.primitive).toBe(collection);
expect(result.modelMatrix).toBeDefined();
expect(result.instanceId).toBe(instanceId);
expect(result.model).toBe(collection._model);
};
}
function expectPick(collection) {
collection.show = false;
expect(scene).notToPick();
collection.show = true;
// Verify each instance
var length = collection.length;
for (var i = 0; i < length; ++i) {
zoomTo(collection, i);
expect(scene).toPickAndCall(verifyPickedInstance(collection, i));
}
}
it('throws if neither options.gltf nor options.url are provided', function() {
expect(function() {
return new ModelInstanceCollection();
}).toThrowDeveloperError();
});
it('throws when both options.gltf and options.url are provided', function() {
return loadModel(boxUrl).then(function(model) {
expect(function() {
return new ModelInstanceCollection({
url: boxUrl,
gltf: model.gltf
});
}).toThrowDeveloperError();
});
});
it('sets properties', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expect(collection.ready).toEqual(true);
expect(collection.show).toEqual(true);
expect(collection.allowPicking).toEqual(true);
expect(collection.length).toEqual(4);
expect(collection.debugShowBoundingVolume).toEqual(false);
expect(collection.debugWireframe).toEqual(false);
expect(collection._dynamic).toEqual(false);
expect(collection._cull).toEqual(true);
expect(collection._model).toBeDefined();
expect(collection._model.ready).toEqual(true);
if (collection._instancingSupported) {
expect(collection._model.cacheKey).toEqual(boxUrl + '#instanced');
}
});
});
it('renders from url', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
});
});
it('renders from Resource', function() {
var resource = new Resource({
url : boxUrl
});
return loadCollection({
url : resource,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
});
});
it('renders from gltf', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
});
});
it('resolves readyPromise', function() {
var collection = scene.primitives.add(new ModelInstanceCollection({
url : boxUrl,
instances : createInstances(4)
}));
return pollToPromise(function() {
scene.renderForSpecs();
return collection._model.ready;
}).then(function() {
scene.renderForSpecs();
scene.renderForSpecs();
return collection.readyPromise.then(function(collection) {
expect(collection.ready).toEqual(true);
});
});
});
it('rejects readyPromise on error', function() {
// Expect promise to be rejected in Model, then in ModelInstanceCollection.
var collection = scene.primitives.add(new ModelInstanceCollection({
url : 'invalid.gltf',
instances : createInstances(4)
}));
collection.update(scene.frameState);
return collection.readyPromise.then(function(collection) {
fail('should not resolve');
}).otherwise(function(error) {
expect(collection.ready).toEqual(false);
});
});
it('renders one instance', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(1)
}).then(function(collection) {
expectRender(collection);
});
});
it('renders zero instances', function() {
var collection = scene.primitives.add(new ModelInstanceCollection({
url : boxUrl,
instances : createInstances(0)
}));
// Collection never reaches the ready state due to returning early
for (var i = 0; i < 10; ++i) {
expectRender(collection, false);
expect(collection.ready).toBe(false);
}
});
it('renders 100 instances', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(100)
}).then(function(collection) {
expectRender(collection);
});
});
it('renders cesiumAir', function() {
return loadCollection({
url : cesiumAirUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
});
});
it('renders rigged figure', function() {
return loadCollection({
url : riggedFigureUrl,
instances : createInstances(4)
}).then(function(collection) {
console.log(collection);
expectRender(collection);
});
});
it('renders when instancing is disabled', function() {
// Disable extension
var instancedArrays = scene.context._instancedArrays;
scene.context._instancedArrays = undefined;
return loadCollection({
url: boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
// Re-enable extension
scene.context._instancedArrays = instancedArrays;
});
});
it('renders when dynamic is true', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4),
dynamic : true
}).then(function(collection) {
expectRender(collection);
});
});
it('verify bounding volume', function() {
var instances = createInstances(4);
return loadCollection({
url : boxUrl,
instances : instances
}).then(function(collection) {
var boundingSphere = getBoundingSphere(instances, boxRadius);
expect(collection._boundingSphere.center).toEqual(boundingSphere.center);
expect(collection._boundingSphere.radius).toEqual(boundingSphere.radius);
});
});
it('renders bounding volume', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
collection.debugShowBoundingVolume = true;
expectRender(collection);
});
});
it('renders in wireframe', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
collection.debugWireframe = true;
scene.renderForSpecs();
expect(collection._drawCommands[0].primitiveType).toEqual(PrimitiveType.LINES);
});
});
it('renders with animations', function() {
// Test that all instances are being animated.
// The moving box is in view on frame 1 and out of view by frame 5.
return loadCollection({
url : movingBoxUrl,
instances : createInstances(4)
}).then(function(collection) {
collection.activeAnimations.addAll();
// Render when animation is in view
var time = JulianDate.now();
expectRender(collection, true, time);
// Render when animation is out of view
time = JulianDate.addSeconds(time, 0.1, new JulianDate());
expectRender(collection, false, time);
});
});
it('renders with animations when instancing is disabled', function() {
// Instance transforms are updated differently when instancing is disabled
// Disable extension
var instancedArrays = scene.context._instancedArrays;
scene.context._instancedArrays = undefined;
return loadCollection({
url : movingBoxUrl,
instances : createInstances(4)
}).then(function(collection) {
collection.activeAnimations.addAll();
// Render when animation is in view
var time = JulianDate.now();
expectRender(collection, true, time);
// Render when animation is out of view
time = JulianDate.addSeconds(time, 0.1, new JulianDate());
expectRender(collection, false, time);
// Re-enable extension
scene.context._instancedArrays = instancedArrays;
});
});
it('renders two model instance collections that use the same cache key', function() {
var collections = [];
var promises = [];
promises.push(loadCollection({
url : boxUrl,
instances : createInstances(2)
}).then(function(collection) {
collections.push(collection);
}));
promises.push(loadCollection({
url : boxUrl,
instances : createInstances(2, 1000.0)
}).then(function(collection) {
collections.push(collection);
}));
return when.all(promises).then(function() {
var resourcesFirst = collections[0]._model._rendererResources;
var resourcesSecond = collections[1]._model._rendererResources;
var name;
expect(collections[0]._model.cacheKey).toEqual(collections[1]._model.cacheKey);
zoomTo(collections[0], 0);
expectRender(collections[0]);
zoomTo(collections[1], 0);
expectRender(collections[1]);
// Check that buffers are equal
for (name in resourcesFirst.buffers) {
if (resourcesFirst.buffers.hasOwnProperty(name)) {
expect(resourcesFirst.buffers[name]).toEqual(resourcesSecond.buffers[name]);
}
}
// Check that programs are equal
for (name in resourcesFirst.programs) {
if (resourcesFirst.programs.hasOwnProperty(name)) {
expect(resourcesFirst.programs[name]).toEqual(resourcesSecond.programs[name]);
}
}
if (collections[0]._instancingSupported) {
// Check that vertex arrays are different, since each collection has a unique vertex buffer for instanced attributes.
for (name in resourcesFirst.vertexArrays) {
if (resourcesFirst.vertexArrays.hasOwnProperty(name)) {
expect(resourcesFirst.vertexArrays[name]).not.toEqual(resourcesSecond.vertexArrays[name]);
}
}
}
});
});
it('culls when out of view and cull is true', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4),
cull : true
}).then(function(collection) {
scene.renderForSpecs();
expect(scene.frustumCommandsList.length).not.toEqual(0);
scene.camera.lookAt(new Cartesian3(100000.0, 0.0, 0.0), new HeadingPitchRange(0.0, 0.0, 10.0));
scene.renderForSpecs();
expect(scene.frustumCommandsList.length).toEqual(0);
});
});
it('does not cull when out of view and cull is false', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4),
cull : false
}).then(function(collection) {
scene.renderForSpecs();
expect(scene.frustumCommandsList.length).not.toEqual(0);
scene.camera.lookAt(new Cartesian3(100000.0, 0.0, 0.0), new HeadingPitchRange(0.0, 0.0, 10.0));
scene.renderForSpecs();
expect(scene.frustumCommandsList.length).not.toEqual(0);
});
});
it('shadows', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
scene.renderForSpecs();
expect(collection._shadows).toBe(ShadowMode.ENABLED);
var drawCommand = collection._drawCommands[0];
expect(drawCommand.castShadows).toBe(true);
expect(drawCommand.receiveShadows).toBe(true);
collection.shadows = ShadowMode.DISABLED;
scene.renderForSpecs();
// Expect commands to have been recreated
drawCommand = collection._drawCommands[0];
expect(drawCommand.castShadows).toBe(false);
expect(drawCommand.receiveShadows).toBe(false);
});
});
it('picks', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectPick(collection);
});
});
it('picks when instancing is disabled', function() {
// Disable extension
var instancedArrays = scene.context._instancedArrays;
scene.context._instancedArrays = undefined;
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectPick(collection);
// Re-enable extension
scene.context._instancedArrays = instancedArrays;
});
});
it('moves instance', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expect(scene).toPickAndCall(function(result) {
var originalMatrix = result.modelMatrix;
result.modelMatrix = Matrix4.IDENTITY;
expect(scene).notToPick();
result.modelMatrix = originalMatrix;
expect(scene).toPickPrimitive(collection);
});
});
});
it('moves instance when instancing is disabled', function() {
// Disable extension
var instancedArrays = scene.context._instancedArrays;
scene.context._instancedArrays = undefined;
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expect(scene).toPickAndCall(function(result) {
var originalMatrix = result.modelMatrix;
var originalRadius = collection._boundingSphere.radius;
result.modelMatrix = Matrix4.IDENTITY;
expect(scene).notToPick();
expect(collection._boundingSphere.radius).toBeGreaterThan(originalRadius);
result.modelMatrix = originalMatrix;
expect(scene).toPickPrimitive(collection);
});
// Re-enable extension
scene.context._instancedArrays = instancedArrays;
});
});
it('renders in 2D', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
scene.morphTo2D(0.0);
expectRender(collection);
});
});
it('renders in 2D when instancing is disabled', function() {
// Disable extension
var instancedArrays = scene.context._instancedArrays;
scene.context._instancedArrays = undefined;
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
scene.morphTo2D(0.0);
expectRender(collection);
// Re-enable extension
scene.context._instancedArrays = instancedArrays;
});
});
it('renders in CV', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
scene.morphToColumbusView(0.0);
expectRender(collection);
});
});
it('renders in CV when instancing is disabled', function() {
// Disable extension
var instancedArrays = scene.context._instancedArrays;
scene.context._instancedArrays = undefined;
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expectRender(collection);
scene.morphToColumbusView(0.0);
expectRender(collection);
// Re-enable extension
scene.context._instancedArrays = instancedArrays;
});
});
it('does not render during morph', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4),
cull : false
}).then(function() {
var commandList = scene.frameState.commandList;
scene.renderForSpecs();
expect(commandList.length).toBeGreaterThan(0);
scene.morphToColumbusView(1.0);
scene.renderForSpecs();
expect(commandList.length).toBe(0);
});
});
it('destroys', function() {
return loadCollection({
url : boxUrl,
instances : createInstances(4)
}).then(function(collection) {
expect(collection.isDestroyed()).toEqual(false);
scene.primitives.remove(collection);
expect(collection.isDestroyed()).toEqual(true);
});
});
}, 'WebGL');