BooleanGeometryBlock throws “same number of properties” when one input has a second UV set (since 8.49.7)
Regression from PR #17805 “Fix CSG2 losing uvs data” (forum thread), shipped in 8.49.7. The Playground below builds on 8.49.6 and earlier (try 7.54.2 from the version menu), and throws on 8.49.7 through the current build.
Playground: Babylon.js Playground
export const createScene = async function () {
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 3, 8, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
await BABYLON.InitializeCSG2Async();
// Any mesh with a second UV set (lightmap UVs from a glTF, for example). A box with uv copied to uv2 is enough.
var source = BABYLON.MeshBuilder.CreateBox("source", { size: 2 }, scene);
source.setVerticesData(BABYLON.VertexBuffer.UV2Kind, source.getVerticesData(BABYLON.VertexBuffer.UVKind).slice());
source.setEnabled(false);
var nodeGeometry = new BABYLON.NodeGeometry("repro");
var meshBlock = new BABYLON.MeshBlock("mesh");
meshBlock.mesh = source;
var cutter = new BABYLON.BoxBlock("cutter"); // positions, normals, uvs only
var boolean = new BABYLON.BooleanGeometryBlock("subtract");
boolean.operation = BABYLON.BooleanGeometryOperations.Subtract;
var output = new BABYLON.GeometryOutputBlock("output");
meshBlock.geometry.connectTo(boolean.geometry0);
cutter.geometry.connectTo(boolean.geometry1);
boolean.output.connectTo(output.geometry);
nodeGeometry.outputBlock = output;
nodeGeometry.build(); // throws: CSG must be used with geometries having the same number of properties
nodeGeometry.createMesh("result", scene);
return scene;
};
What changed
Before 8.49.7, CSG2._Construct looked up the secondary UV sets as data[kind], with kind being "uv2", while the VertexData property is "uvs2". It never found them, so it assumed no mesh had a second UV channel and dropped uv2 from both operands. That hid a gap in BooleanGeometryBlock._buildBlock: the “Ensure that all the fields are filled” block pads normals, uvs and colors when only one operand has them, but not uvs2 through uvs6.
PR #17805 fixed the lookup, so CSG2 now counts uv2 (10 properties vs 8 here) and _process refuses the mismatch the boolean node never padded. The CSG2 fix is right; the node just needs the matching change.
Suggested fix (booleanGeometryBlock.ts, next to the existing padding):
for (const key of ["uvs2", "uvs3", "uvs4", "uvs5", "uvs6"] as const) {
if (!vertexData0[key] && vertexData1[key]) {
vertexData0[key] = new Array((vertexData0.positions.length / 3) * 2);
}
if (!vertexData1[key] && vertexData0[key]) {
vertexData1[key] = new Array((vertexData1.positions.length / 3) * 2);
}
}
One more in the same padding code: vertexCount is taken from geometry0 but sizes the uvs and colors pads for geometry1. With a 24-vertex box (with uvs) as geometry0 and a 231-vertex sphere (uvs removed) as geometry1, the sphere gets a 48-float uv pad. On 7.32.5 the result had 364 NaN uv values out of 412; on 9.25.0 with manifold 3.4.0 the build throws “Error while creating the CSG: Non-finite vertex”. The normals pads already use each operand’s own positions.length, so the uvs and colors pads just need the same treatment.