Can you fix the javascript so the camera/player collides with the terrain?:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Voxel-based 3D World</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        canvas {
            display: block;
        }
    </style>
</head>

<body>

    <script src="https://cdn.jsdelivr.net/npm/three@0.154/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/simplex-noise@2.4.0/simplex-noise.js"></script>
    <script>
        // Ensure the libraries are loaded
        if (!THREE || !CANNON || !SimplexNoise) {
            alert('Failed to load required libraries.');
        }

        // 1. Setup Three.js Scene
        var scene = new THREE.Scene();
        scene.background = new THREE.Color(0x87ceeb); // Sky blue color

        var camera = new THREE.PerspectiveCamera(
            90, // Field of view
            window.innerWidth / window.innerHeight, // Aspect ratio
            0.1, // Near clipping plane
            1000 // Far clipping plane
        );

        var renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 2. Initialize Cannon.js Physics World
        var physicsWorld = new CANNON.World();
        physicsWorld.gravity.set(0, -9.82, 0); // Standard Earth gravity (negative Y)

        // Define a physics material with some friction
        var defaultMaterial = new CANNON.Material('defaultMaterial');
        var defaultContactMaterial = new CANNON.ContactMaterial(
            defaultMaterial,
            defaultMaterial,
            {
                friction: 0.4,
                restitution: 0.0,
            }
        );
        physicsWorld.defaultContactMaterial = defaultContactMaterial;
        physicsWorld.addContactMaterial(defaultContactMaterial);

        // 3. Chunk Management for Terrain
        var chunkSize = 16;
        var renderDistance = 2;
        var chunks = {};

        var simplex = new SimplexNoise(); // Noise function for terrain

        function getChunkID(x, z) {
            var chunkX = Math.floor(x / chunkSize);
            var chunkZ = Math.floor(z / chunkSize);
            return chunkX + '' + chunkZ;
        }

        function createGroundChunk(chunkX, chunkZ) {
            // Create height data
            var size = chunkSize + 1;
            var heights = [];
            for (var i = 0; i < size; i++) {
                heights[i] = [];
                for (var j = 0; j < size; j++) {
                    var x = (chunkX * chunkSize) + i;
                    var z = (chunkZ * chunkSize) + j;
                    var y = Math.floor(simplex.noise2D(x / 50, z / 50) * 5);
                    heights[i][j] = y;
                }
            }

            // Create geometry
            var geometry = new THREE.PlaneGeometry(
                chunkSize,
                chunkSize,
                chunkSize,
                chunkSize
            );
            geometry.rotateX(-Math.PI / 2);

            // Update vertex positions based on height data
            var vertices = geometry.attributes.position;
            for (var i = 0; i < vertices.count; i++) {
                var vertex = new THREE.Vector3();
                vertex.fromBufferAttribute(vertices, i);
                var xi = Math.round((vertex.x + chunkSize / 2));
                var zi = Math.round((vertex.z + chunkSize / 2));
                var y = heights[xi][zi];
                vertices.setY(i, y);
            }
            geometry.computeVertexNormals();

            // Create mesh
            var material = new THREE.MeshLambertMaterial({ color: 0x228B22 });
            var mesh = new THREE.Mesh(geometry, material);
            mesh.position.set(
                chunkX * chunkSize,
                0,
                chunkZ * chunkSize
            );
            scene.add(mesh);

            // Create physics shape
            var shape = createHeightfieldShape(heights);
            var body = new CANNON.Body({ mass: 0, material: defaultMaterial });
            body.addShape(shape);
            // Correct the body position to match the mesh position
            body.position.set(
                mesh.position.x - chunkSize / 2,
                0,
                mesh.position.z - chunkSize / 2
            );
            physicsWorld.addBody(body);

            // Store the chunk
            chunks[getChunkID(chunkX * chunkSize, chunkZ * chunkSize)] = {
                mesh: mesh,
                body: body,
                chunkX: chunkX,
                chunkZ: chunkZ,
            };
        }

        // Helper function to create a heightfield shape
        function createHeightfieldShape(data) {
            var matrix = data;
            var shape = new CANNON.Heightfield(matrix, {
                elementSize: 1,
                // Specify upAxis to align with Y-axis (Three.js and Cannon.js both use Y-up)
                // By default, upAxis is 1 (Y-axis) in newer versions of Cannon.js
                upAxis: 1
            });
            return shape;
        }

        // Function to load chunks around the player
        function loadChunks(playerPosition) {
            var currentChunkX = Math.floor(playerPosition.x / chunkSize);
            var currentChunkZ = Math.floor(playerPosition.z / chunkSize);
            for (var dx = -renderDistance; dx <= renderDistance; dx++) {
                for (var dz = -renderDistance; dz <= renderDistance; dz++) {
                    var chunkX = currentChunkX + dx;
                    var chunkZ = currentChunkZ + dz;
                    var chunkID = chunkX + '' + chunkZ;
                    if (!chunks[chunkID]) {
                        createGroundChunk(chunkX, chunkZ);
                    }
                }
            }
        }

        // 4. Player Setup
        // Create player physics body
        var playerShape = new CANNON.Sphere(1);
        var playerBody = new CANNON.Body({
            mass: 1,
            material: defaultMaterial,
            shape: playerShape,
        });
        playerBody.position.set(0, 20, 0); // Start higher to avoid initial collision
        playerBody.fixedRotation = true; // Prevent tipping over
        physicsWorld.addBody(playerBody);

        // Create a dummy THREE.Mesh (invisible player representation)
        var playerMesh = new THREE.Mesh(
            new THREE.BoxGeometry(1, 2, 1),
            new THREE.MeshBasicMaterial({ visible: false })
        );
        scene.add(playerMesh);

        // 5. Controls and Movement
        var keysPressed = {};
        var movementSpeed = 10;
        var jumpSpeed = 8;

        // Event listeners for key presses
        document.addEventListener('keydown', function (event) {
            keysPressed[event.code] = true;
        });

        document.addEventListener('keyup', function (event) {
            keysPressed[event.code] = false;
        });

        // Mouse movement variables
        var rotation = { x: 0, y: 0 };
        var rotationSpeed = { x: 0, y: 0 };
        var rotationDamping = 0.1;

        // Event listeners for mouse movement
        var pointerLocked = false;

        document.addEventListener('click', function () {
            // Request pointer lock for mouse look
            if (!pointerLocked) {
                var canvas = renderer.domElement;
                canvas.requestPointerLock = canvas.requestPointerLock ||
                    canvas.mozRequestPointerLock ||
                    canvas.webkitRequestPointerLock;
                canvas.requestPointerLock();
            }
        });

        document.addEventListener('pointerlockchange', function () {
            pointerLocked = document.pointerLockElement === renderer.domElement;
        }, false);

        document.addEventListener('mousemove', function (event) {
            if (pointerLocked) {
                var movementX = event.movementX || event.mozMovementX || 0;
                var movementY = event.movementY || event.mozMovementY || 0;
                rotationSpeed.y -= movementX * 0.002;
                rotationSpeed.x -= movementY * 0.002;
            }
        }, false);

        // 6. Camera Setup
        camera.position.set(0, 0, 0);
        camera.lookAt(new THREE.Vector3(0, 2, 0));

        function updateCameraPosition() {
            // Smooth camera follow
            var targetPosition = new THREE.Vector3(
                playerBody.position.x,
                playerBody.position.y + 2,
                playerBody.position.z
            );

            camera.position.lerp(targetPosition, 0.1);

            // Apply rotation with damping
            rotation.x += rotationSpeed.x;
            rotation.y += rotationSpeed.y;

            // Limit vertical rotation
            var piHalf = Math.PI / 2 - 0.1;
            rotation.x = Math.max(-piHalf, Math.min(piHalf, rotation.x));

            // Reset rotation speed with damping
            rotationSpeed.x *= (1 - rotationDamping);
            rotationSpeed.y *= (1 - rotationDamping);

            camera.rotation.order = 'YXZ';
            camera.rotation.y = rotation.y;
            camera.rotation.x = rotation.x;
        }

        // 7. Day-Night Cycle
        var dayDuration = 60; // Duration in seconds
        var timeOfDay = 0; // Time counter

        // Create sun and moon
        var sun = new THREE.DirectionalLight(0xffffff, 1);
        var moon = new THREE.DirectionalLight(0x8888ff, 0.2);

        sun.position.set(0, 1, 0);
        moon.position.set(0, -1, 0);

        scene.add(sun);
        scene.add(moon);

        // Create sun and moon meshes
        var sunMesh = new THREE.Mesh(
            new THREE.SphereGeometry(5),
            new THREE.MeshBasicMaterial({ color: 0xffff00 })
        );
        var moonMesh = new THREE.Mesh(
            new THREE.SphereGeometry(5),
            new THREE.MeshBasicMaterial({ color: 0x8888ff })
        );
        scene.add(sunMesh);
        scene.add(moonMesh);

        // Create stars
        var starGeometry = new THREE.BufferGeometry();
        var starMaterial = new THREE.PointsMaterial({ color: 0xffffff });
        var starVertices = [];
        for (var i = 0; i < 1000; i++) {
            starVertices.push(
                THREE.MathUtils.randFloatSpread(2000),
                THREE.MathUtils.randFloatSpread(2000),
                THREE.MathUtils.randFloatSpread(2000)
            );
        }
        starGeometry.setAttribute(
            'position',
            new THREE.Float32BufferAttribute(starVertices, 3)
        );
        var stars = new THREE.Points(starGeometry, starMaterial);
        scene.add(stars);

        // Update day-night cycle
        function updateDayNightCycle(deltaTime) {
            timeOfDay += deltaTime;
            var cycleProgress = (timeOfDay % dayDuration) / dayDuration;
            var angle = cycleProgress * Math.PI * 2;

            // Update sun and moon positions
            sun.position.set(
                Math.cos(angle) * 100,
                Math.sin(angle) * 100,
                0
            );
            moon.position.set(
                Math.cos(angle + Math.PI) * 100,
                Math.sin(angle + Math.PI) * 100,
                0
            );

            sunMesh.position.copy(sun.position);
            moonMesh.position.copy(moon.position);

            // Adjust light intensities
            var sunIntensity = Math.max(0, Math.sin(angle));
            var moonIntensity = Math.max(0, -Math.sin(angle));

            sun.intensity = sunIntensity;
            moon.intensity = moonIntensity * 0.5; // Moon is dimmer

            // Change sky color
            var skyColor = new THREE.Color();
            skyColor.lerpColors(
                new THREE.Color(0x000000), // Night color
                new THREE.Color(0x87ceeb), // Day color
                sunIntensity
            );
            scene.background = skyColor;

            // Show/hide stars
            stars.visible = sunIntensity < 0.1;
        }

        // Adjust day-night cycle duration with J key and arrow keys
        document.addEventListener('keydown', function (event) {
            if (event.code === 'KeyJ') {
                if (keysPressed['ArrowUp']) {
                    dayDuration += 10;
                } else if (keysPressed['ArrowDown']) {
                    dayDuration = Math.max(10, dayDuration - 10);
                }
            }
        });

        // 8. Block Placement and Removal
        // Raycaster for block placement/removal
        var raycaster = new THREE.Raycaster();
        var pointer = new THREE.Vector2();

        // Function to create a physical block
        var blocks = [];

        function createPhysicalBlock(x, y, z) {
            var geometry = new THREE.BoxGeometry(1, 1, 1);
            var material = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
            var mesh = new THREE.Mesh(geometry, material);
            mesh.position.set(x, y, z);
            scene.add(mesh);

            var shape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5));
            var body = new CANNON.Body({ mass: 0, material: defaultMaterial });
            body.addShape(shape);
            body.position.set(x, y, z);
            physicsWorld.addBody(body);

            blocks.push({ mesh: mesh, body: body });
        }

        // Mouse event handlers
        function onPointerDown(event) {
            if (!pointerLocked) return;

            event.preventDefault();

            // Update raycaster
            raycaster.setFromCamera({ x: 0, y: 0 }, camera);

            // Objects to intersect
            var objects = [];
            for (var key in chunks) {
                objects.push(chunks[key].mesh);
            }
            for (var i = 0; i < blocks.length; i++) {
                objects.push(blocks[i].mesh);
            }

            // Check for intersections
            var intersects = raycaster.intersectObjects(objects, false);

            if (intersects.length > 0) {
                var intersect = intersects[0];
                var point = intersect.point;
                var normal = intersect.face.normal;
                var position = new THREE.Vector3().copy(point).add(normal.multiplyScalar(0.5)).floor().addScalar(0.5);

                if (event.button === 0) {
                    // Left-click to place a block
                    createPhysicalBlock(position.x, position.y, position.z);
                } else if (event.button === 2) {
                    // Right-click to remove a block
                    if (intersect.object !== undefined) {
                        var obj = intersect.object;
                        // Find block index
                        var index = -1;
                        for (var i = 0; i < blocks.length; i++) {
                            if (blocks[i].mesh === obj) {
                                index = i;
                                break;
                            }
                        }
                        if (index !== -1) {
                            physicsWorld.removeBody(blocks[index].body);
                            scene.remove(blocks[index].mesh);
                            blocks.splice(index, 1);
                        }
                    }
                }
            }
        }

        window.addEventListener('mousedown', onPointerDown);
        window.addEventListener('contextmenu', function (event) { event.preventDefault(); });

        // 9. Lighting and Environment
        // Ambient light
        var ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
        scene.add(ambientLight);

        // Adjust ambient light during day-night cycle
        function updateAmbientLight() {
            ambientLight.intensity = 0.2 + (sun.intensity * 0.8);
        }

        // 10. Event Handling
        // Handle window resize events
        window.addEventListener('resize', onWindowResize);

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // 11. Animation Loop
        var clock = new THREE.Clock();

        function animate() {
            requestAnimationFrame(animate);

            var deltaTime = clock.getDelta();
            var elapsedTime = clock.elapsedTime;

            // Update physics world
            var timeStep = 1 / 60;
            physicsWorld.step(timeStep, deltaTime, 3);

            // Update player movement
            updatePlayer(deltaTime);

            // Update camera position and rotation
            updateCameraPosition();

            // Load and unload chunks based on player's position
            loadChunks(playerBody.position);

            // Update day-night cycle
            updateDayNightCycle(deltaTime);
            updateAmbientLight();

            // Sync playerMesh position with physics body
            playerMesh.position.copy(playerBody.position);

            // Render the scene
            renderer.render(scene, camera);
        }

        animate();

        // 12. Helper Functions
        // Update player function
        function updatePlayer(deltaTime) {
            var moveForward = keysPressed['KeyW'];
            var moveBackward = keysPressed['KeyS'];
            var moveLeft = keysPressed['KeyA'];
            var moveRight = keysPressed['KeyD'];
            var jump = keysPressed['Space'];

            var direction = new THREE.Vector3();

            if (moveForward) {
                direction.z -= 1;
            }
            if (moveBackward) {
                direction.z += 1;
            }
            if (moveLeft) {
                direction.x -= 1;
            }
            if (moveRight) {
                direction.x += 1;
            }
            direction.normalize();

            // Rotate direction based on camera rotation
            var quaternion = new THREE.Quaternion();
            quaternion.setFromEuler(new THREE.Euler(0, rotation.y, 0, 'YXZ'));
            direction.applyQuaternion(quaternion);

            // Apply movement
            var velocity = playerBody.velocity;

            // Ground friction
            velocity.x *= 0.9;
            velocity.z *= 0.9;

            velocity.x += direction.x * movementSpeed * deltaTime;
            velocity.z += direction.z * movementSpeed * deltaTime;

            // Jumping
            if (jump && isPlayerOnGround()) {
                velocity.y = jumpSpeed;
            }
        }

        // Check if player is on the ground
        function isPlayerOnGround() {
            var ray = new CANNON.Ray();
            ray.from.copy(playerBody.position);
            ray.to.set(playerBody.position.x, playerBody.position.y - 1.1, playerBody.position.z);

            var result = new CANNON.RaycastResult();
            ray.intersectWorld(physicsWorld, {
                collisionFilterMask: -1,
                skipBackfaces: true,
            }, result);

            return result.hasHit;
        }

        // Start by loading initial chunks
        loadChunks(playerBody.position);
    </script>

</body>

</html>