From 7a0ed77629db57a768f9842b37e2161932eaeb85 Mon Sep 17 00:00:00 2001 From: Dr Tracy Gardner Date: Thu, 25 Jul 2024 15:25:33 +0000 Subject: [PATCH] Improved raycasting for better ground detection --- dev-dist/sw.js | 2 +- flock.js | 121 ++++++++++++++++++++++++++++++++++++++++++++----- main.js | 97 ++++++++------------------------------- 3 files changed, 130 insertions(+), 90 deletions(-) diff --git a/dev-dist/sw.js b/dev-dist/sw.js index c929e6b..d1ca939 100644 --- a/dev-dist/sw.js +++ b/dev-dist/sw.js @@ -91,7 +91,7 @@ define(['./workbox-07658ed7'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.l30l2oi1398" + "revision": "0.3l98b1qdp6o" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/flock.js b/flock.js index b3d7aa0..2f562ca 100644 --- a/flock.js +++ b/flock.js @@ -342,7 +342,7 @@ function createCapsuleFromBoundingBox(mesh, scene) { boundingInfo.boundingBox.minimumWorld.z; // Calculate the radius as the min of the width and depth - const radius = Math.max(width, depth) / 2; + const radius = Math.min(width, depth) / 2; // Calculate the effective height of the capsule's cylindrical part const cylinderHeight = Math.max(0, height - 2 * radius); @@ -749,31 +749,32 @@ export function isTouchingSurface(modelName) { } function checkIfOnSurface(mesh) { - // Get the bounding box of the mesh + // Compute the world matrix to ensure updated positions mesh.computeWorldMatrix(true); const boundingInfo = mesh.getBoundingInfo(); + // Setting the ray origin slightly above the mesh's lowest point const minY = boundingInfo.boundingBox.minimumWorld.y; - //console.log("Min Y", minY); - // Cast the ray from a point slightly below the bottom of the mesh const rayOrigin = new BABYLON.Vector3( boundingInfo.boundingBox.centerWorld.x, - minY, + minY + 0.01, // Slightly above to avoid hitting the mesh itself boundingInfo.boundingBox.centerWorld.z, ); - rayOrigin.y -= 0.01; - // Adjust the ray origin slightly below the mesh's bottom - // Raycast downwards - const ray = new BABYLON.Ray(rayOrigin, new BABYLON.Vector3(0, -1, 0), 0.02); + // Cast the ray downwards with sufficient length to cover expected distances + const ray = new BABYLON.Ray(rayOrigin, new BABYLON.Vector3(0, -1, 0), 2); //const rayHelper = new BABYLON.RayHelper(ray); //rayHelper.show(window.scene); + + // Perform the raycast + mesh.isPickable = false; const hit = window.scene.pickWithRay(ray); + mesh.isPickable = true; - //console.log(`Raycasting from: ${rayOrigin.toString()}`); //console.log(`Ray hit: ${hit.hit}, Distance: ${hit.distance}, Picked Mesh: ${hit.pickedMesh ? hit.pickedMesh.name : "None"}`,); - return hit.hit && hit.pickedMesh !== null && hit.distance <= 0.1; + // Check if the mesh is directly on top of the surface + return hit.hit && hit.pickedMesh !== null && hit.distance <= 0.02; // Adjust the threshold as needed } export function keyPressed(key) { @@ -842,3 +843,101 @@ export async function changeColour(modelName, color) { } }); } + +// Helper function to move the model forward +export function moveForward(modelName, speed) { + const model = window.scene.getMeshByName(modelName); + if (!model || speed === 0) return; + + const forwardSpeed = speed; + const cameraForward = window.scene.activeCamera + .getForwardRay() + .direction.normalize(); + const moveDirection = cameraForward.scale(forwardSpeed); + const currentVelocity = model.physics.getLinearVelocity(); + + // Set linear velocity + model.physics.setLinearVelocity( + new BABYLON.Vector3( + moveDirection.x, + currentVelocity.y, + moveDirection.z, + ), + ); + + // Set rotation + const facingDirection = + speed >= 0 + ? new BABYLON.Vector3( + -cameraForward.x, + 0, + -cameraForward.z, + ).normalize() + : new BABYLON.Vector3( + cameraForward.x, + 0, + cameraForward.z, + ).normalize(); + const targetRotation = BABYLON.Quaternion.FromLookDirectionLH( + facingDirection, + BABYLON.Vector3.Up(), + ); + const currentRotation = model.rotationQuaternion; + const deltaRotation = targetRotation.multiply(currentRotation.conjugate()); + const deltaEuler = deltaRotation.toEulerAngles(); + model.physics.setAngularVelocity( + new BABYLON.Vector3(0, deltaEuler.y * 5, 0), + ); // Adjust scalar as needed + model.rotationQuaternion.x = 0; + model.rotationQuaternion.z = 0; + model.rotationQuaternion.normalize(); // Re-normalize the quaternion +} + +export function attachCamera(modelName) { + window.whenModelReady(modelName, function (mesh) { + if (mesh) { + // After physics calculations, adjust velocities and rotations + window.scene.onAfterPhysicsObservable.add(() => { + const currentVelocity = mesh.physics.getLinearVelocity(); + const newVelocity = new BABYLON.Vector3( + 0, + currentVelocity.y, + 0, + ); // Maintain vertical velocity, reset horizontal + mesh.physics.setLinearVelocity(newVelocity); + mesh.physics.setAngularVelocity(BABYLON.Vector3.Zero()); + + const currentRotationQuaternion = mesh.rotationQuaternion; + const currentEulerRotation = + currentRotationQuaternion.toEulerAngles(); + const newRotationQuaternion = + BABYLON.Quaternion.RotationYawPitchRoll( + currentEulerRotation.y, + 0, + 0, + ); + mesh.rotationQuaternion.copyFrom(newRotationQuaternion); + }); + + // Set up camera + const camera = new BABYLON.ArcRotateCamera( + "camera", + Math.PI / 2, + Math.PI / 4, + 10, + mesh.position, + window.scene, + ); + camera.checkCollisions = true; + camera.lowerBetaLimit = Math.PI / 2.5; // Lower angle limit + camera.upperBetaLimit = Math.PI / 2; // Upper angle limit + camera.lowerRadiusLimit = 2; + camera.upperRadiusLimit = 7; + camera.setTarget(mesh.position); + camera.attachControl(canvas, true); + window.scene.activeCamera = camera; + } else { + console.log("Model not loaded:", modelName); + } + }); +} diff --git a/main.js b/main.js index 659e3a0..c21d539 100644 --- a/main.js +++ b/main.js @@ -39,6 +39,8 @@ import { randomColour, scaleMesh, changeColour, + moveForward, + attachCamera, } from "./flock.js"; import { toolbox } from "./toolbox.js"; import { FlowGraphLog10Block } from "babylonjs"; @@ -70,6 +72,8 @@ window.randomColour = randomColour; window.scaleMesh = scaleMesh; window.changeColour = changeColour; window.newCharacter = newCharacter; +window.moveForward = moveForward; +window.attachCamera = attachCamera; registerFieldColour(); @@ -196,28 +200,6 @@ const modelNames = [ //"bear_anim.glb", ]; -/* -#e49085 -#ab8b64 -#cc9863 -#d1a17f -#eac083 -#e6bd91 -#db9d9e -#d7977a -#ffb5a2 -#e6b7ae -#d97c57 -#eeb4a8 -#fdc8b8 -#efa19a -#ffdbd9 -#c8734c -#f8d4ce -#eda898 -#ee959b -*/ - console.log("Welcome to Flock 🐑🐑🐑"); workspace.addChangeListener(function (event) { @@ -2666,62 +2648,15 @@ javascriptGenerator.forBlock["switch_animation"] = function (block) { javascriptGenerator.forBlock["move_forward"] = function (block) { const modelName = javascriptGenerator.nameDB_.getName( block.getFieldValue("MODEL"), - Blockly.Names.NameType.VARIABLE, + Blockly.Names.NameType.VARIABLE ); - const speed = - javascriptGenerator.valueToCode( - block, - "SPEED", - javascriptGenerator.ORDER_ATOMIC, - ) || "0"; - return ` - - const model = window.scene.getMeshByName(${modelName}); - - if (model) { - - if (${speed} === 0){ return; } - - const forwardSpeed = -${speed}; - const cameraForward = window.scene.activeCamera.getForwardRay().direction.normalize(); - - // Forward direction adjusted to move away from the camera - const moveDirection = cameraForward.scale(-forwardSpeed); - const currentVelocity = model.physics.getLinearVelocity(); - model.physics.setLinearVelocity( - new BABYLON.Vector3( - moveDirection.x, - currentVelocity.y, - moveDirection.z - ) - ); - - // Decide the facing direction based on whether steps is positive or negative - let facingDirection; - if (${speed} >= 0) { - // Face away from the camera when moving forward - facingDirection = new BABYLON.Vector3(-cameraForward.x, 0, -cameraForward.z).normalize(); - } else { - // Face towards the camera when moving backward - facingDirection = new BABYLON.Vector3(cameraForward.x, 0, cameraForward.z).normalize(); - } - - // Calculate the target rotation from the facing direction - const targetRotation = BABYLON.Quaternion.FromLookDirectionLH(facingDirection, BABYLON.Vector3.Up()); - const currentRotation = model.rotationQuaternion; - const deltaRotation = targetRotation.multiply(currentRotation.conjugate()); - const deltaEuler = deltaRotation.toEulerAngles(); - const scaledAngularVelocityY = new BABYLON.Vector3(0, deltaEuler.y * 5, 0); // Adjust the scalar as needed - - // Update angular velocity for rotation - model.physics.setAngularVelocity(scaledAngularVelocityY); - - model.rotationQuaternion.x = 0; - model.rotationQuaternion.z = 0; - model.rotationQuaternion.normalize(); // Re-normalize the quaternion to maintain a valid rotation + const speed = javascriptGenerator.valueToCode( + block, + "SPEED", + javascriptGenerator.ORDER_ATOMIC + ) || "0"; - } - `; + return `moveForward(${modelName}, ${speed});\n`; }; javascriptGenerator.forBlock["up"] = function (block) { @@ -2743,6 +2678,14 @@ javascriptGenerator.forBlock["touching_surface"] = function (block) { return [`isTouchingSurface(${modelName})`, javascriptGenerator.ORDER_NONE]; }; +javascriptGenerator.forBlock["camera_follow2"] = function (block) { + const modelName = javascriptGenerator.nameDB_.getName( + block.getFieldValue("MESH_VAR"), + Blockly.Names.NameType.VARIABLE + ); + return `attachCamera(${modelName});\n`; +}; + javascriptGenerator.forBlock["camera_follow"] = function (block) { const modelName = javascriptGenerator.nameDB_.getName( block.getFieldValue("MESH_VAR"), @@ -2778,8 +2721,6 @@ window.scene.onAfterPhysicsObservable.add(() => { mesh.rotationQuaternion.copyFrom(newRotationQuaternion); }); - - const camera = new BABYLON.ArcRotateCamera("camera", Math.PI / 2, Math.PI / 4, -20, mesh.position, window.scene); camera.checkCollisions = true;