I am trying to recreate this react flip https://r3f-animated-book-slider-final.vercel.app/ in threejs, it is going well but I have som z-fighing that is not happening in react and I have no clue how to fix it …
The math is perfect but visually is a mess.
This is how I set the z so it is perfect, I can’t offset it it must be exactly like this so that the math is right, if I offset it it will not look as a book anymore…
mesh.position.z = (-i * this.pageDepth - this.curPage * this.pageDepth);
I can’t share the code on glitch so I paste the code here hoping for some help.
addPages() {
// Raycaster
this.raycaster = new FWDFB_THREE.Raycaster();
// Create a new group for the book and add it to the scene.
this.bookGroup = new FWDFB_THREE.Group();
this.bookGroup.name = "bookGroup";
this.scene.add(this.bookGroup);
// Arrays to store page meshes
this.pagesAR = [];
this.curPage = 0;
// Assume texturesAR has pairs of textures (front then back for each page)
this.totalPages = Math.floor(this.texturesAR.length / 2);
const pageWidth = this.data.pageWidth;
const pageHeight = this.data.pageHeight;
this.pageDepth = this.data.pageDepth; // e.g. 0.003 (a very thin value)
const pageSegments = 30;
const segmentWidth = pageWidth / pageSegments;
// Create the book page geometry – a thin box – and translate it so its left edge is at x=0.
const geometry = new FWDFB_THREE.BoxGeometry(
pageWidth,
pageHeight,
this.pageDepth,
pageSegments,
2
);
geometry.translate(pageWidth / 2, 0, 0);
// Prepare skinning attributes (the same for every page).
const positionAttr = geometry.attributes.position;
const vertex = new FWDFB_THREE.Vector3();
const skinIndexes = [];
const skinWeights = [];
for (let i = 0; i < positionAttr.count; i++) {
vertex.fromBufferAttribute(positionAttr, i);
const x = vertex.x;
let rawIndex = Math.floor(x / segmentWidth);
rawIndex = Math.min(rawIndex, pageSegments - 2); // clamp so we don’t exceed available bones
const skinIndex = rawIndex;
let skinWeight = (x % segmentWidth) / segmentWidth;
skinIndexes.push(skinIndex, skinIndex + 1, 0, 0);
skinWeights.push(1 - skinWeight, skinWeight, 0, 0);
}
geometry.setAttribute('skinIndex', new FWDFB_THREE.Uint16BufferAttribute(skinIndexes, 4));
geometry.setAttribute('skinWeight', new FWDFB_THREE.Float32BufferAttribute(skinWeights, 4));
// Define some colors.
const whiteColor = "#fff";
const emissiveColor = "#ffffff";
// Create base materials for the non-textured faces.
const pageMaterials = [
new FWDFB_THREE.MeshStandardMaterial({ color: whiteColor }),
new FWDFB_THREE.MeshStandardMaterial({ color: whiteColor }),
new FWDFB_THREE.MeshStandardMaterial({ color: whiteColor }),
new FWDFB_THREE.MeshStandardMaterial({ color: whiteColor }),
];
// Loop over pages (each page uses a pair of textures: front and back).
for (let i = 0; i < this.totalPages; i++) {
// Get front and back textures.
const frontTexture = this.texturesAR[2 * i].texture;
const backTexture = this.texturesAR[2 * i + 1].texture;
// Create the front face material.
const frontMaterial = new FWDFB_THREE.MeshStandardMaterial({
color: whiteColor,
map: frontTexture,
...(i === 0 ? { roughnessMap: this.roughnessTexture } : { roughness: 0.1 }),
emissive: emissiveColor,
emissiveIntensity: 0,
depthTest: true,
});
// Create the back face material.
const backMaterial = new FWDFB_THREE.MeshStandardMaterial({
color: whiteColor,
map: backTexture,
...(i === this.totalPages - 1 ? { roughnessMap: this.roughnessTexture } : { roughness: 0.1 }),
emissive: emissiveColor,
emissiveIntensity: 0,
depthTest: true,
});
// Combine base materials with our front and back materials.
const materials = [
...pageMaterials,
frontMaterial,
backMaterial,
];
// Create bones for the skinned mesh (one per segment).
const bones = [];
for (let j = 0; j < pageSegments; j++) {
const bone = new FWDFB_THREE.Bone();
bones.push(bone);
bone.position.x = (j === 0) ? 0 : segmentWidth;
if (j > 0) bones[j - 1].add(bone);
}
const skeleton = new FWDFB_THREE.Skeleton(bones);
// Create the skinned mesh.
const mesh = new FWDFB_THREE.SkinnedMesh(geometry, materials);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.frustumCulled = false;
// Position pages using your desired formula.
// Here we use:
mesh.position.z = (-i * this.pageDepth - this.curPage * this.pageDepth);
// Add the root bone and bind the skeleton.
mesh.add(skeleton.bones[0]);
mesh.bind(skeleton);
// Store and add the mesh.
this.pagesAR.push({
mesh,
front: this.texturesAR[2 * i],
back: this.texturesAR[2 * i + 1],
});
this.bookGroup.add(mesh);
}
}