cesium/Source/Scene/ModelExperimental/ModelExperimentalSceneGraph.js

284 lines
8.0 KiB
JavaScript

import buildDrawCommands from "./buildDrawCommands.js";
import BoundingSphere from "../../Core/BoundingSphere.js";
import Check from "../../Core/Check.js";
import CPUStylingPipelineStage from "./CPUStylingPipelineStage.js";
import defaultValue from "../../Core/defaultValue.js";
import defined from "../../Core/defined.js";
import Matrix4 from "../../Core/Matrix4.js";
import CustomShaderPipelineStage from "./CustomShaderPipelineStage.js";
import LightingPipelineStage from "./LightingPipelineStage.js";
import ModelColorPipelineStage from "./ModelColorPipelineStage.js";
import ModelExperimentalPrimitive from "./ModelExperimentalPrimitive.js";
import ModelExperimentalNode from "./ModelExperimentalNode.js";
import ModelExperimentalUtility from "./ModelExperimentalUtility.js";
import ModelRenderResources from "./ModelRenderResources.js";
import NodeRenderResources from "./NodeRenderResources.js";
import PrimitiveRenderResources from "./PrimitiveRenderResources.js";
/**
* An in memory representation of the scene graph for a {@link ModelExperimental}
*
* @param {Object} options An object containing the following options
* @param {ModelExperimental} options.model The model this scene graph belongs to
* @param {ModelComponents} options.modelComponents The model components describing the model
*
* @alias ModelExperimentalSceneGraph
* @constructor
*
* @private
*/
export default function ModelExperimentalSceneGraph(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object("options.model", options.model);
Check.typeOf.object("options.modelComponents", options.modelComponents);
//>>includeEnd('debug');
/**
* A reference to the {@link ModelExperimental} that owns this scene graph.
*
* @type {ModelExperimental}
* @readonly
*
* @private
*/
this._model = options.model;
/**
* The model components that represent the contents of the 3D model file.
*
* @type {ModelComponents}
* @readonly
*
* @private
*/
this._modelComponents = options.modelComponents;
/**
* Pipeline stages to apply across the model.
*
* @type {Object[]}
* @readonly
*
* @private
*/
this._pipelineStages = [];
/**
* The runtime nodes that make up the scene graph
*
* @type {ModelExperimentalNode[]}
* @readonly
*
* @private
*/
this._runtimeNodes = [];
/**
* Once computed, the {@link DrawCommand}s that are used to render this
* scene graph are stored here.
*
* @type {DrawCommand[]}
* @readonly
*
* @private
*/
this._drawCommands = [];
/**
* The bounding sphere containing all the primitives in the scene graph.
*
* @type {BoundingSphere}
* @readonly
*
* @private
*/
this._boundingSphere = undefined;
/**
* The array of bounding spheres of all the primitives in the scene graph.
*
* @type {BoundingSphere[]}
* @readonly
*
* @private
*/
this._boundingSpheres = [];
initialize(this);
}
function initialize(sceneGraph) {
var modelMatrix = Matrix4.clone(sceneGraph._model.modelMatrix);
var scene = sceneGraph._modelComponents.scene;
ModelExperimentalUtility.correctModelMatrix(
modelMatrix,
scene.upAxis,
scene.forwardAxis
);
var rootNodes = sceneGraph._modelComponents.scene.nodes;
for (var i = 0; i < rootNodes.length; i++) {
var rootNode = sceneGraph._modelComponents.scene.nodes[i];
var rootNodeModelMatrix = Matrix4.multiply(
modelMatrix,
ModelExperimentalUtility.getNodeTransform(rootNode),
new Matrix4()
);
traverseSceneGraph(sceneGraph, rootNode, rootNodeModelMatrix);
}
}
/**
* Recursively traverse through the nodes in the scene graph, using depth-first
* post-order traversal.
*
* @param {ModelSceneGraph} sceneGraph The scene graph
* @param {ModelComponents.Node} node The current node
* @param {Matrix4} modelMatrix The current computed model matrix for this node.
*
* @private
*/
function traverseSceneGraph(sceneGraph, node, modelMatrix) {
// No processing needs to happen if node has no children and no mesh primitives.
if (!defined(node.children) && !defined(node.primitives)) {
return;
}
// Traverse through scene graph.
var i;
if (defined(node.children)) {
for (i = 0; i < node.children.length; i++) {
var childNode = node.children[i];
var childNodeModelMatrix = Matrix4.multiply(
modelMatrix,
ModelExperimentalUtility.getNodeTransform(childNode),
new Matrix4()
);
traverseSceneGraph(sceneGraph, childNode, childNodeModelMatrix);
}
}
// Process node and mesh primitives.
var runtimeNode = new ModelExperimentalNode({
node: node,
modelMatrix: modelMatrix,
});
if (defined(node.primitives)) {
for (i = 0; i < node.primitives.length; i++) {
runtimeNode.runtimePrimitives.push(
new ModelExperimentalPrimitive({
primitive: node.primitives[i],
node: node,
model: sceneGraph._model,
})
);
}
}
sceneGraph._runtimeNodes.push(runtimeNode);
}
/**
* Generates the draw commands for each primitive in the model.
*
* @param {FrameState} frameState The current frame state. This is needed to
* allocate GPU resources as needed.
*
* @private
*/
ModelExperimentalSceneGraph.prototype.buildDrawCommands = function (
frameState
) {
var modelRenderResources = new ModelRenderResources(this._model);
var modelPipelineStages = [];
var model = this._model;
if (defined(model.color)) {
modelPipelineStages.push(ModelColorPipelineStage);
}
var i, j, k;
for (i = 0; i < modelPipelineStages.length; i++) {
var modelPipelineStage = modelPipelineStages[i];
modelPipelineStage.process(modelRenderResources, model, frameState);
}
for (i = 0; i < this._runtimeNodes.length; i++) {
var runtimeNode = this._runtimeNodes[i];
var nodeRenderResources = new NodeRenderResources(
modelRenderResources,
runtimeNode
);
for (j = 0; j < runtimeNode.pipelineStages.length; j++) {
var nodePipelineStage = runtimeNode.pipelineStages[j];
nodePipelineStage.process(
nodeRenderResources,
runtimeNode.node,
frameState
);
}
for (j = 0; j < runtimeNode.runtimePrimitives.length; j++) {
var runtimePrimitive = runtimeNode.runtimePrimitives[j];
// The pipeline stage array is copied because we don't want dynamic stages to be added to the primitive's default stages.
var primitivePipelineStages = runtimePrimitive.pipelineStages.slice();
if (defined(model.style) && !defined(model.customShader)) {
// Ensure that the CPU styling stage is always added before the AlphaPipelineStage, which is the last stage.
primitivePipelineStages.splice(
runtimePrimitive.pipelineStages.length - 1,
0,
CPUStylingPipelineStage
);
}
if (defined(model.customShader) && !defined(model.style)) {
// The custom shader stage needs to go before the lighting stage.
var lightingStageIndex = primitivePipelineStages.indexOf(
LightingPipelineStage
);
primitivePipelineStages.splice(
lightingStageIndex,
0,
CustomShaderPipelineStage
);
}
var primitiveRenderResources = new PrimitiveRenderResources(
nodeRenderResources,
runtimePrimitive
);
for (k = 0; k < primitivePipelineStages.length; k++) {
var primitivePipelineStage = primitivePipelineStages[k];
primitivePipelineStage.process(
primitiveRenderResources,
runtimePrimitive.primitive,
frameState
);
}
this._boundingSpheres.push(primitiveRenderResources.boundingSphere);
var drawCommands = buildDrawCommands(
primitiveRenderResources,
frameState
);
this._drawCommands.push.apply(this._drawCommands, drawCommands);
}
}
this._boundingSphere = BoundingSphere.fromBoundingSpheres(
this._boundingSpheres
);
};