@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
119 lines (99 loc) • 3.92 kB
JavaScript
import combine from "../../Core/combine.js";
import ShaderDestination from "../../Renderer/ShaderDestination.js";
import SkinningStageVS from "../../Shaders/Model/SkinningStageVS.js";
import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
/**
* The skinning pipeline stage processes the joint matrices of a skinned primitive.
*
* @namespace SkinningPipelineStage
*
* @private
*/
const SkinningPipelineStage = {
name: "SkinningPipelineStage", // Helps with debugging
FUNCTION_ID_GET_SKINNING_MATRIX: "getSkinningMatrix",
FUNCTION_SIGNATURE_GET_SKINNING_MATRIX: "mat4 getSkinningMatrix()",
};
/**
* This pipeline stage processes the joint matrices of a skinned primitive, adding
* the relevant functions and uniforms to the shaders. The joint and weight attributes
* themselves are processed in the geometry pipeline stage.
*
* Processes a primitive. This stage modifies the following parts of the render resources:
* <ul>
* <li> adds the uniform declaration for the joint matrices in the vertex shader</li>
* <li> adds the function to compute the skinning matrix in the vertex shader</li>
* </ul>
*
* @param {PrimitiveRenderResources} renderResources The render resources for this primitive.
* @param {ModelComponents.Primitive} primitive The primitive.
* @private
*/
SkinningPipelineStage.process = function (renderResources, primitive) {
const shaderBuilder = renderResources.shaderBuilder;
shaderBuilder.addDefine("HAS_SKINNING", undefined, ShaderDestination.VERTEX);
addGetSkinningMatrixFunction(shaderBuilder, primitive);
const runtimeNode = renderResources.runtimeNode;
const jointMatrices = runtimeNode.computedJointMatrices;
shaderBuilder.addUniform(
"mat4",
`u_jointMatrices[${jointMatrices.length}]`,
ShaderDestination.VERTEX,
);
shaderBuilder.addVertexLines(SkinningStageVS);
const uniformMap = {
u_jointMatrices: function () {
return runtimeNode.computedJointMatrices;
},
};
renderResources.uniformMap = combine(uniformMap, renderResources.uniformMap);
};
function getMaximumAttributeSetIndex(primitive) {
let setIndex = -1;
const attributes = primitive.attributes;
const length = attributes.length;
for (let i = 0; i < length; i++) {
const attribute = attributes[i];
const isJointsOrWeights =
attribute.semantic === VertexAttributeSemantic.JOINTS ||
attribute.semantic === VertexAttributeSemantic.WEIGHTS;
if (!isJointsOrWeights) {
continue;
}
setIndex = Math.max(setIndex, attribute.setIndex);
}
return setIndex;
}
function addGetSkinningMatrixFunction(shaderBuilder, primitive) {
shaderBuilder.addFunction(
SkinningPipelineStage.FUNCTION_ID_GET_SKINNING_MATRIX,
SkinningPipelineStage.FUNCTION_SIGNATURE_GET_SKINNING_MATRIX,
ShaderDestination.VERTEX,
);
const initialLine = "mat4 skinnedMatrix = mat4(0);";
shaderBuilder.addFunctionLines(
SkinningPipelineStage.FUNCTION_ID_GET_SKINNING_MATRIX,
[initialLine],
);
let setIndex;
let componentIndex;
const componentStrings = ["x", "y", "z", "w"];
const maximumSetIndex = getMaximumAttributeSetIndex(primitive);
for (setIndex = 0; setIndex <= maximumSetIndex; setIndex++) {
for (componentIndex = 0; componentIndex <= 3; componentIndex++) {
const component = componentStrings[componentIndex];
// Example: skinnedMatrix += a_weights_0.x * u_jointMatrices[int(a_joints_0.x)];
const line = `skinnedMatrix += a_weights_${setIndex}.${component} * u_jointMatrices[int(a_joints_${setIndex}.${component})];`;
shaderBuilder.addFunctionLines(
SkinningPipelineStage.FUNCTION_ID_GET_SKINNING_MATRIX,
[line],
);
}
}
const returnLine = "return skinnedMatrix;";
shaderBuilder.addFunctionLines(
SkinningPipelineStage.FUNCTION_ID_GET_SKINNING_MATRIX,
[returnLine],
);
}
export default SkinningPipelineStage;