So What im trying to do is, displace a normal ground mesh, to create realistic terrain and then create a physics body in that same shape in rapier (This parts working), after apply textures to the visual mesh
But what ends up happening is if i set the diffues or normal map of the following textures to repeat 5 or something different, it distorts the visual mesh and the physics mesh stays the same as diaplced
I tried cloneing the ground mesh then applying, but same issue. Seems the issue is the displacement is done by the shader in three js, so its basically dynamic? and keeps changing based on new data
Code here:
// Function to create the ground mesh using the heightmap
function createGround(world) {
// Hardcoded values
const widthSeg = 100; // Number of segments along width
const heightSeg = 100; // Number of segments along height
const horTexture = 1; // Horizontal texture repeat
const verTexture = 1; // Vertical texture repeat
const dispScale = 90; // Displacement scale
// Create plane geometry
const groundGeo = new THREE.PlaneGeometry(1000, 1000, widthSeg, heightSeg);
// Create material (without displacement map set yet)
const groundMat = new THREE.MeshStandardMaterial({
color: 0xe00e00, // Red color
wireframe: false, // Wireframe mode
displacementScale: dispScale // Displacement intensity
});
// Create and configure the ground mesh
const groundMesh = new THREE.Mesh(groundGeo, groundMat);
scene.add(groundMesh);
groundMesh.rotation.x = -Math.PI / 2; // Rotate to lie flat
groundMesh.position.y = -0.5; // Shift slightly below origin
// Load heightmap texture asynchronously
new THREE.TextureLoader().setPath("assets/textures/").load("heightmap4.jpg", (dispMap) => {
// Configure texture wrapping and repeat
dispMap.wrapS = dispMap.wrapT = THREE.RepeatWrapping;
dispMap.repeat.set(horTexture, verTexture);
// Assign displacement map to material and update
groundMat.displacementMap = dispMap;
groundMat.needsUpdate = true;
// Extract height data from the texture
const canvas = document.createElement("canvas");
canvas.width = dispMap.image.width;
canvas.height = dispMap.image.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(dispMap.image, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
// Number of vertices is segments + 1
const nrows = heightSeg + 1; // 101 rows
const ncolumns = widthSeg + 1; // 101 columns
const heights = new Float32Array(nrows * ncolumns);
// Sample texture at each vertex’s UV coordinates
for (let j = 0; j < nrows; j++) {
for (let i = 0; i < ncolumns; i++) {
const u = i / widthSeg; // UV from 0 to 1
const v = j / heightSeg; // UV from 0 to 1
// const texX = Math.floor(u * dispMap.image.width);
// const texY = Math.floor(v * dispMap.image.height);
const texX = Math.min(Math.floor(u * dispMap.image.width), dispMap.image.width - 1);
const texY = Math.min(Math.floor(v * dispMap.image.height), dispMap.image.height - 1);
const index = (texY * dispMap.image.width + texX) * 4; // RGBA, so *4
const heightValue = pixels[index] / 255; // Red channel (0-1), assuming grayscale
heights[j * ncolumns + i] = heightValue * dispScale; // Scale to match Three.js
}
}
//working basic plane
//const planeColliderDesc = RAPIER.ColliderDesc.cuboid(1000, 0.1, 1000);
//world.createCollider(planeColliderDesc, terrainRigidBody);
//console.log("Plane collider created, debug vertices:", world.debugRender().vertices.length);
// Create the heightfield collider
// Rapier expects a different structure for the scale
const halfExtentsX = 500; // half of your 1000 size
const halfExtentsZ = 500; // half of your 1000 size
// 1. Create static rigid body
const terrainRigidBodyDesc = RAPIER.RigidBodyDesc.newStatic()
.setTranslation(0, -0.5, 0); // Match your mesh position
const terrainRigidBody = world.createRigidBody(terrainRigidBodyDesc);
const transformedHeights = new Float32Array(heights.length);
for (let j = 0; j < nrows; j++) {
for (let i = 0; i < ncolumns; i++) {
// 90° clockwise + horizontal flip
// ends up as: newRow = i, newCol = j
transformedHeights[i * nrows + j] = heights[j * ncolumns + i];
}
}
// Create the heightfield collider - note the correct parameter order
const terrainColliderDesc = RAPIER.ColliderDesc.heightfield(
nrows - 1,
ncolumns - 1,
transformedHeights, // Use transformed array
{ x: 1000, y: 1, z: 1000 } // Use normalized scale (y=1) and apply height scale later
);
//terrainColliderDesc.setScale(1.0, dispScale, 1.0);
// Create the collider
world.createCollider(terrainColliderDesc, terrainRigidBody);
// Define the scale for the heightfield
const scale = { x: 1000, y: 1000 };
// Create the heightfield collider
//const terrainColliderDesc = RAPIER.ColliderDesc.heightfield(nrows, ncolumns, heights, scale);
//world.createCollider(terrainColliderDesc, terrainRigidBody);
});
}
This is without the texture adding part as I don’t know how best to apply it without breaking the visual height of the mesh