runAt method
Vector4
runAt(
- int vertexIndex,
- int instanceIndex,
- Float32List a,
- ShaderBindings bindings,
- Float32List out,
override
The same as CpuVertexShader.run, plus the vertex's and the instance's own indices.
run must still work — a caller that has no indices, or a rasteriser that has not been taught to pass them, falls back to it — so an implementation answers both and shares whatever it can.
Implementation
@override
Vector4 runAt(
int vertexIndex,
int instanceIndex,
Float32List a,
ShaderBindings bindings,
Float32List out,
) {
// The weights are renormalised rather than trusted: an exporter that
// writes three of four and leaves the fourth at zero is common, and a
// total below one shrinks the vertex towards the origin.
final total =
a[_weights] + a[_weights + 1] + a[_weights + 2] + a[_weights + 3];
final w = total > 1e-5
? <double>[
a[_weights] / total,
a[_weights + 1] / total,
a[_weights + 2] / total,
a[_weights + 3] / total,
]
: <double>[1.0, 0.0, 0.0, 0.0];
// Every slot is read, a zero weight included, and the index is clamped
// into the palette: `JointIndex` in the shader, line for line.
final skin = Matrix4.zero();
for (var i = 0; i < 4; i++) {
final joint = bindings.mat4(
'SkinInfo',
'joint_matrices',
at: jointIndex(a[_joints + i]),
);
for (var e = 0; e < 16; e++) {
skin[e] = skin[e] + joint[e] * w[i];
}
}
final model = bindings.mat4('FrameInfo', 'model');
final mvp = bindings.mat4('FrameInfo', 'mvp');
final normalMatrix = bindings.mat4('FrameInfo', 'normal_matrix');
// Morphed in the rest pose and skinned afterwards, which is the order glTF
// specifies and the only one that composes: a face morphs where it was
// modelled and the skeleton then carries it.
final morphed =
vertexIndex >= 0 &&
_morphScratch.load(
vertexIndex,
a,
bindings,
positionAt: kPosition,
normalAt: kNormal,
tangentAt: kTangent,
);
final local = morphed
? Vector4(
_morphScratch.position.x,
_morphScratch.position.y,
_morphScratch.position.z,
1.0,
)
: Vector4(a[kPosition], a[kPosition + 1], a[kPosition + 2], 1.0);
final Vector4 skinned = skin * local;
final Vector4 world = model * skinned;
out[kVWorld] = world.x;
out[kVWorld + 1] = world.y;
out[kVWorld + 2] = world.z;
final skinRotation = skin.getRotation();
final Vector3 n =
normalMatrix.getRotation() *
(skinRotation *
(morphed
? _morphScratch.normal
: Vector3(a[kNormal], a[kNormal + 1], a[kNormal + 2])));
out[kVNormal] = n.x;
out[kVNormal + 1] = n.y;
out[kVNormal + 2] = n.z;
out[kVUv] = a[kTexcoord];
out[kVUv + 1] = a[kTexcoord + 1];
for (var i = 0; i < 4; i++) {
out[kVColour + i] = a[kColour + i];
}
out[kVLightmap] = 0.0;
out[kVLightmap + 1] = 0.0;
for (var i = 0; i < 4; i++) {
out[kVInstance + i] = 0.0;
}
final Vector3 t =
model.getRotation() *
(skinRotation *
(morphed
? Vector3(
_morphScratch.tangent.x,
_morphScratch.tangent.y,
_morphScratch.tangent.z,
)
: Vector3(a[kTangent], a[kTangent + 1], a[kTangent + 2])));
out[kVTangent] = t.x;
out[kVTangent + 1] = t.y;
out[kVTangent + 2] = t.z;
final mirrored =
(model.getRotation().determinant() < 0.0) !=
(skinRotation.determinant() < 0.0);
out[kVTangent + 3] = mirrored ? -a[kTangent + 3] : a[kTangent + 3];
return mvp * skinned;
}