import defined from "../../Core/defined.js";
|
import PrimitiveType from "../../Core/PrimitiveType.js";
|
import AttributeType from "../AttributeType.js";
|
import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
|
import GeometryStageFS from "../../Shaders/ModelExperimental/GeometryStageFS.js";
|
import GeometryStageVS from "../../Shaders/ModelExperimental/GeometryStageVS.js";
|
import FeatureIdPipelineStage from "./FeatureIdPipelineStage.js";
|
import ShaderDestination from "../../Renderer/ShaderDestination.js";
|
import ModelExperimentalUtility from "./ModelExperimentalUtility.js";
|
|
/**
|
* The geometry pipeline stage processes the vertex attributes of a primitive.
|
*
|
* @namespace GeometryPipelineStage
|
*
|
* @private
|
*/
|
var GeometryPipelineStage = {};
|
GeometryPipelineStage.name = "GeometryPipelineStage"; // Helps with debugging
|
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_VS =
|
"ProcessedAttributesVS";
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_FS =
|
"ProcessedAttributesFS";
|
GeometryPipelineStage.STRUCT_NAME_PROCESSED_ATTRIBUTES = "ProcessedAttributes";
|
GeometryPipelineStage.FUNCTION_ID_INITIALIZE_ATTRIBUTES =
|
"initializeAttributes";
|
GeometryPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_ATTRIBUTES =
|
"void initializeAttributes(out ProcessedAttributes attributes)";
|
GeometryPipelineStage.FUNCTION_ID_SET_DYNAMIC_VARYINGS_VS =
|
"setDynamicVaryingsVS";
|
GeometryPipelineStage.FUNCTION_ID_SET_DYNAMIC_VARYINGS_FS =
|
"setDynamicVaryingsFS";
|
GeometryPipelineStage.FUNCTION_SIGNATURE_SET_DYNAMIC_VARYINGS =
|
"void setDynamicVaryings(inout ProcessedAttributes attributes)";
|
|
/**
|
* This pipeline stage processes the vertex attributes of a primitive, adding the attribute declarations to the shaders,
|
* the attribute objects to the render resources and setting the flags as needed.
|
*
|
* Processes a primitive. This stage modifies the following parts of the render resources:
|
* <ul>
|
* <li> adds attribute and varying declarations for the vertex attributes in the vertex and fragment shaders
|
* <li> creates the objects required to create VertexArrays
|
* <li> sets the flag for point primitive types
|
* </ul>
|
*
|
* @param {PrimitiveRenderResources} renderResources The render resources for this primitive.
|
* @param {ModelComponents.Primitive} primitive The primitive.
|
*
|
* @private
|
*/
|
GeometryPipelineStage.process = function (renderResources, primitive) {
|
var shaderBuilder = renderResources.shaderBuilder;
|
// These structs are similar, though the fragment shader version has a couple
|
// additional fields.
|
shaderBuilder.addStruct(
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_VS,
|
"ProcessedAttributes",
|
ShaderDestination.VERTEX
|
);
|
shaderBuilder.addStruct(
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_FS,
|
"ProcessedAttributes",
|
ShaderDestination.FRAGMENT
|
);
|
|
// The Feature struct is always added since it's required for compilation. It may be unused if features are not present.
|
shaderBuilder.addStruct(
|
FeatureIdPipelineStage.STRUCT_ID_FEATURE,
|
FeatureIdPipelineStage.STRUCT_NAME_FEATURE,
|
ShaderDestination.BOTH
|
);
|
|
// This initialization function is only needed in the vertex shader,
|
// it assigns the non-quantized attribute struct fields from the
|
// physical attributes
|
shaderBuilder.addFunction(
|
GeometryPipelineStage.FUNCTION_ID_INITIALIZE_ATTRIBUTES,
|
GeometryPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_ATTRIBUTES,
|
ShaderDestination.VERTEX
|
);
|
|
// Positions in other coordinate systems need more variables
|
shaderBuilder.addVarying("vec3", "v_positionWC");
|
shaderBuilder.addVarying("vec3", "v_positionEC");
|
shaderBuilder.addStructField(
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_FS,
|
"vec3",
|
"positionWC"
|
);
|
shaderBuilder.addStructField(
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_FS,
|
"vec3",
|
"positionEC"
|
);
|
|
// Though they have identical signatures, the implementation is different
|
// between vertex and fragment shaders. The VS stores attributes in
|
// varyings, while the FS unpacks the varyings for use by other stages.
|
shaderBuilder.addFunction(
|
GeometryPipelineStage.FUNCTION_ID_SET_DYNAMIC_VARYINGS_VS,
|
GeometryPipelineStage.FUNCTION_SIGNATURE_SET_DYNAMIC_VARYINGS,
|
ShaderDestination.VERTEX
|
);
|
shaderBuilder.addFunction(
|
GeometryPipelineStage.FUNCTION_ID_SET_DYNAMIC_VARYINGS_FS,
|
GeometryPipelineStage.FUNCTION_SIGNATURE_SET_DYNAMIC_VARYINGS,
|
ShaderDestination.FRAGMENT
|
);
|
|
var index;
|
for (var i = 0; i < primitive.attributes.length; i++) {
|
var attribute = primitive.attributes[i];
|
if (attribute.semantic === VertexAttributeSemantic.POSITION) {
|
index = 0;
|
} else {
|
// The attribute index is taken from the node render resources, which may have added some attributes of its own.
|
index = renderResources.attributeIndex++;
|
}
|
processAttribute(renderResources, attribute, index);
|
}
|
|
handleBitangents(shaderBuilder, primitive.attributes);
|
|
if (primitive.primitiveType === PrimitiveType.POINTS) {
|
shaderBuilder.addDefine("PRIMITIVE_TYPE_POINTS");
|
}
|
|
shaderBuilder.addVertexLines([GeometryStageVS]);
|
shaderBuilder.addFragmentLines([GeometryStageFS]);
|
};
|
|
function processAttribute(renderResources, attribute, attributeIndex) {
|
var shaderBuilder = renderResources.shaderBuilder;
|
var attributeInfo = ModelExperimentalUtility.getAttributeInfo(attribute);
|
|
addAttributeToRenderResources(renderResources, attribute, attributeIndex);
|
addAttributeDeclaration(shaderBuilder, attributeInfo);
|
addVaryingDeclaration(shaderBuilder, attributeInfo);
|
|
// For common attributes like positions, normals and tangents, the code is
|
// already in GeometryStageVS, we just need to enable it
|
if (defined(attribute.semantic)) {
|
addSemanticDefine(shaderBuilder, attribute);
|
}
|
|
// Some GLSL code must be dynamically generated
|
updateAttributesStruct(shaderBuilder, attributeInfo);
|
updateInitialzeAttributesFunction(shaderBuilder, attributeInfo);
|
updateSetDynamicVaryingsFunction(shaderBuilder, attributeInfo);
|
}
|
|
function addSemanticDefine(shaderBuilder, attribute) {
|
var semantic = attribute.semantic;
|
var setIndex = attribute.setIndex;
|
switch (semantic) {
|
case VertexAttributeSemantic.NORMAL:
|
shaderBuilder.addDefine("HAS_NORMALS");
|
break;
|
case VertexAttributeSemantic.TANGENT:
|
shaderBuilder.addDefine("HAS_TANGENTS");
|
break;
|
case VertexAttributeSemantic.FEATURE_ID:
|
case VertexAttributeSemantic.TEXCOORD:
|
case VertexAttributeSemantic.COLOR:
|
shaderBuilder.addDefine("HAS_" + semantic + "_" + setIndex);
|
}
|
}
|
|
function addAttributeToRenderResources(
|
renderResources,
|
attribute,
|
attributeIndex
|
) {
|
var quantization = attribute.quantization;
|
var type;
|
var componentDatatype;
|
if (defined(quantization)) {
|
type = quantization.type;
|
componentDatatype = quantization.componentDatatype;
|
} else {
|
type = attribute.type;
|
componentDatatype = attribute.componentDatatype;
|
}
|
|
var semantic = attribute.semantic;
|
var setIndex = attribute.setIndex;
|
if (
|
semantic === VertexAttributeSemantic.FEATURE_ID &&
|
setIndex >= renderResources.featureIdVertexAttributeSetIndex
|
) {
|
renderResources.featureIdVertexAttributeSetIndex = setIndex + 1;
|
}
|
|
var vertexAttribute = {
|
index: attributeIndex,
|
value: defined(attribute.buffer) ? undefined : attribute.constant,
|
vertexBuffer: attribute.buffer,
|
componentsPerAttribute: AttributeType.getNumberOfComponents(type),
|
componentDatatype: componentDatatype,
|
offsetInBytes: attribute.byteOffset,
|
strideInBytes: attribute.byteStride,
|
normalize: attribute.normalized,
|
};
|
|
renderResources.attributes.push(vertexAttribute);
|
}
|
|
function addVaryingDeclaration(shaderBuilder, attributeInfo) {
|
var variableName = attributeInfo.variableName;
|
var varyingName = "v_" + variableName;
|
|
var glslType;
|
if (variableName === "normalMC") {
|
// though the attribute is in model coordinates, the varying is
|
// in eye coordinates.
|
varyingName = "v_normalEC";
|
glslType = attributeInfo.glslType;
|
} else if (variableName === "tangentMC") {
|
// Tangent's glslType is vec4, but in the shader it is split into
|
// vec3 tangent and vec3 bitangent
|
glslType = "vec3";
|
// like normalMC, the varying is converted to eye coordinates
|
varyingName = "v_tangentEC";
|
} else {
|
glslType = attributeInfo.glslType;
|
}
|
|
shaderBuilder.addVarying(glslType, varyingName);
|
}
|
|
function addAttributeDeclaration(shaderBuilder, attributeInfo) {
|
var semantic = attributeInfo.attribute.semantic;
|
var variableName = attributeInfo.variableName;
|
|
var attributeName;
|
var glslType;
|
if (attributeInfo.isQuantized) {
|
attributeName = "a_quantized_" + variableName;
|
glslType = attributeInfo.quantizedGlslType;
|
} else {
|
attributeName = "a_" + variableName;
|
glslType = attributeInfo.glslType;
|
}
|
|
if (semantic === VertexAttributeSemantic.POSITION) {
|
shaderBuilder.setPositionAttribute(glslType, attributeName);
|
} else {
|
shaderBuilder.addAttribute(glslType, attributeName);
|
}
|
}
|
|
function updateAttributesStruct(shaderBuilder, attributeInfo) {
|
var vsStructId = GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_VS;
|
var fsStructId = GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_FS;
|
var variableName = attributeInfo.variableName;
|
if (variableName === "color") {
|
// Always declare color as a vec4, even if it was a vec3
|
shaderBuilder.addStructField(vsStructId, "vec4", "color");
|
shaderBuilder.addStructField(fsStructId, "vec4", "color");
|
} else if (variableName === "tangentMC") {
|
// declare tangent as vec3, the w component is only used for computing
|
// the bitangent. Also, the tangent is in model coordinates in the vertex
|
// shader but in eye space in the fragment coordinates
|
shaderBuilder.addStructField(vsStructId, "vec3", "tangentMC");
|
shaderBuilder.addStructField(fsStructId, "vec3", "tangentEC");
|
} else if (variableName === "normalMC") {
|
// Normals are in model coordinates in the vertex shader but in eye
|
// coordinates in the fragment shader
|
shaderBuilder.addStructField(vsStructId, "vec3", "normalMC");
|
shaderBuilder.addStructField(fsStructId, "vec3", "normalEC");
|
} else {
|
shaderBuilder.addStructField(
|
vsStructId,
|
attributeInfo.glslType,
|
variableName
|
);
|
shaderBuilder.addStructField(
|
fsStructId,
|
attributeInfo.glslType,
|
variableName
|
);
|
}
|
}
|
|
function updateInitialzeAttributesFunction(shaderBuilder, attributeInfo) {
|
if (attributeInfo.isQuantized) {
|
// Skip initialization, it will be handled in the dequantization stage.
|
return;
|
}
|
|
var functionId = GeometryPipelineStage.FUNCTION_ID_INITIALIZE_ATTRIBUTES;
|
var variableName = attributeInfo.variableName;
|
var line;
|
if (variableName === "tangentMC") {
|
line = "attributes.tangentMC = a_tangentMC.xyz;";
|
} else {
|
line = "attributes." + variableName + " = a_" + variableName + ";";
|
}
|
shaderBuilder.addFunctionLines(functionId, [line]);
|
}
|
|
function updateSetDynamicVaryingsFunction(shaderBuilder, attributeInfo) {
|
var semantic = attributeInfo.attribute.semantic;
|
var setIndex = attributeInfo.attribute.setIndex;
|
if (defined(semantic) && !defined(setIndex)) {
|
// positions, normals, and tangents are handled statically in
|
// GeometryStageVS
|
return;
|
}
|
|
// In the vertex shader, we want things like
|
// v_texCoord_1 = attributes.texCoord_1;
|
var functionId = GeometryPipelineStage.FUNCTION_ID_SET_DYNAMIC_VARYINGS_VS;
|
var variableName = attributeInfo.variableName;
|
var line = "v_" + variableName + " = attributes." + variableName + ";";
|
shaderBuilder.addFunctionLines(functionId, [line]);
|
|
// In the fragment shader, we do the opposite:
|
// attributes.texCoord_1 = v_texCoord_1;
|
functionId = GeometryPipelineStage.FUNCTION_ID_SET_DYNAMIC_VARYINGS_FS;
|
line = "attributes." + variableName + " = v_" + variableName + ";";
|
shaderBuilder.addFunctionLines(functionId, [line]);
|
}
|
|
function handleBitangents(shaderBuilder, attributes) {
|
var hasNormals = false;
|
var hasTangents = false;
|
for (var i = 0; i < attributes.length; i++) {
|
var attribute = attributes[i];
|
if (attribute.semantic === VertexAttributeSemantic.NORMAL) {
|
hasNormals = true;
|
} else if (attribute.semantic === VertexAttributeSemantic.TANGENT) {
|
hasTangents = true;
|
}
|
}
|
|
// Bitangents are only defined if we have normals and tangents
|
if (!hasNormals || !hasTangents) {
|
return;
|
}
|
|
shaderBuilder.addDefine("HAS_BITANGENTS");
|
|
// compute the bitangent according to the formula in the glTF spec
|
shaderBuilder.addFunctionLines(
|
GeometryPipelineStage.FUNCTION_ID_INITIALIZE_ATTRIBUTES,
|
[
|
"attributes.bitangentMC = normalize(cross(a_normalMC, a_tangentMC.xyz) * a_tangentMC.w);",
|
]
|
);
|
|
shaderBuilder.addVarying("vec3", "v_bitangentEC");
|
shaderBuilder.addStructField(
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_VS,
|
"vec3",
|
"bitangentMC"
|
);
|
shaderBuilder.addStructField(
|
GeometryPipelineStage.STRUCT_ID_PROCESSED_ATTRIBUTES_FS,
|
"vec3",
|
"bitangentEC"
|
);
|
}
|
|
export default GeometryPipelineStage;
|