From 49e048627702b33118b0492560255346660595ff Mon Sep 17 00:00:00 2001 From: goynov Date: Thu, 16 Oct 2025 09:53:52 +0300 Subject: [PATCH] physics test2 --- package-lock.json | 31 ++- package.json | 2 +- public/static/meshes/maze-reed.bin | Bin 2800 -> 2800 bytes public/static/meshes/maze-reed.gltf | 176 +++++++++--------- src/components/GameDesigner/GameDesigner.vue | 18 +- src/components/GamePlaying/GamePlaying.vue | 2 +- .../MazeQuizGame/MazeObject.js | 62 +++--- .../MazeQuizGame/MazeQuizGame.js | 4 +- src/lib/Clickable.js | 2 - src/lib/Hero.js | 24 +-- src/lib/Physics.js | 104 +++++++++++ src/lib/gameEngine.js | 27 ++- 12 files changed, 283 insertions(+), 169 deletions(-) create mode 100644 src/lib/Physics.js diff --git a/package-lock.json b/package-lock.json index fadd729..c1bc719 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "three-viewport-gizmo": "^2.2.0", "uuid": "^11.0.2", "vue": "^3.5.13", - "vuetify": "^3.7.16" + "vuetify": "^3.10.5" }, "devDependencies": { "@dimforge/rapier3d": "^0.18.2", @@ -2070,9 +2070,9 @@ "license": "MIT" }, "node_modules/@vuetify/loader-shared": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz", - "integrity": "sha512-Ss3GC7eJYkp2SF6xVzsT7FAruEmdihmn4OCk2+UocREerlXKWgOKKzTN5PN3ZVN5q05jHHrsNhTuWbhN61Bpdg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.1.1.tgz", + "integrity": "sha512-jSZTzTYaoiv8iwonFCVZQ0YYX/M+Uyl4ng+C4egMJT0Hcmh9gIxJL89qfZICDeo3g0IhqrvipW2FFKKRDMtVcA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -8000,13 +8000,13 @@ } }, "node_modules/vite-plugin-vuetify": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.0.4.tgz", - "integrity": "sha512-A4cliYUoP/u4AWSRVRvAPKgpgR987Pss7LpFa7s1GvOe8WjgDq92Rt3eVXrvgxGCWvZsPKziVqfHHdCMqeDhfw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.2.tgz", + "integrity": "sha512-I/wd6QS+DO6lHmuGoi1UTyvvBTQ2KDzQZ9oowJQEJ6OcjWfJnscYXx2ptm6S7fJSASuZT8jGRBL3LV4oS3LpaA==", "devOptional": true, "license": "MIT", "dependencies": { - "@vuetify/loader-shared": "^2.0.3", + "@vuetify/loader-shared": "^2.1.1", "debug": "^4.3.3", "upath": "^2.0.1" }, @@ -8198,22 +8198,19 @@ } }, "node_modules/vuetify": { - "version": "3.7.16", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.16.tgz", - "integrity": "sha512-Few/cBtgJYgdkzi0LWmVy67G5uc2+q7oWcadbcTUPAtEtGYNh2AM28h01Fk+ScJgfxkA077//ZDff1rh3jYG/w==", + "version": "3.10.5", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.10.5.tgz", + "integrity": "sha512-3h1onfjcj04B+LJ0cXvGrOOe6C3652JAlyUAuGKgQXwhP9Oe1hAfdM6+svXR5Dt1xC39lQEGqJdacZtwisUEoQ==", "license": "MIT", - "engines": { - "node": "^12.20 || >=14.13" - }, "funding": { "type": "github", "url": "https://github.com/sponsors/johnleider" }, "peerDependencies": { "typescript": ">=4.7", - "vite-plugin-vuetify": ">=1.0.0", - "vue": "^3.3.0", - "webpack-plugin-vuetify": ">=2.0.0" + "vite-plugin-vuetify": ">=2.1.0", + "vue": "^3.5.0", + "webpack-plugin-vuetify": ">=3.1.0" }, "peerDependenciesMeta": { "typescript": { diff --git a/package.json b/package.json index a60bf04..6de653e 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "three-viewport-gizmo": "^2.2.0", "uuid": "^11.0.2", "vue": "^3.5.13", - "vuetify": "^3.7.16" + "vuetify": "^3.10.5" }, "devDependencies": { "@dimforge/rapier3d": "^0.18.2", diff --git a/public/static/meshes/maze-reed.bin b/public/static/meshes/maze-reed.bin index f4fc6c14da2260cffda692f4812d749a222485d1..3db612fb2714c66f01b769e2aa2c5bd3c995b5e0 100644 GIT binary patch delta 338 zcmew$`ayJq2eZMYGd_FS3fT5$Z;;%71j;_yBx%1J$Sz-CVh>`Ugt9?uCO2>>P1a-O z+w8--gjK?NqvZa)Wd`<%XV2Jm?~~l$AfRFcVo#pKtT4HRSwI0MXmRGuz7-{s_8vuErk`*2E3t^wi>P9B~}sD#CtGy5i|a4IN(7%NI7 z?HdGCY|z;Z3=R7yKj0Lb%*VwunTbncvK^P?RIjtSz#gRDHNV>)q#ncvsR#3c z>KPfB88{gjfta0vm4ShQ4M?&va5FG5urP2jFqr}g4u diff --git a/src/components/GamePlaying/GamePlaying.vue b/src/components/GamePlaying/GamePlaying.vue index 5fae3b3..f6894e6 100644 --- a/src/components/GamePlaying/GamePlaying.vue +++ b/src/components/GamePlaying/GamePlaying.vue @@ -231,7 +231,7 @@ export default { ]) maze.load().then(o=>{ gameEngine.activeObjects.add(o); - o.scale.set(5,5,5); + //o.scale.set(5,5,5); }) new Grass(Grass.positions(1000,50,50), '/static/textures/grass01.png', 1, .5).then(mesh=>{ diff --git a/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js b/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js index ae44783..bc812a9 100644 --- a/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js +++ b/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js @@ -5,12 +5,15 @@ import { TextObject } from '../TextObject'; class MazeObject { constructor(engine, def, params = {}){ let room = new Group(); - let scene = room; + let root = room; this.object = room; let context = {}; - context.wallSize = params.wallSize || .65; - context.tubeSize = params.tubeSize || .8; - context.wallDepth = params.wallDepth || .1; + + const scale = 5; + + context.wallSize = params.wallSize || .65*scale; + context.tubeSize = params.tubeSize || .8*scale; + context.wallDepth = params.wallDepth || .1*scale; context.fontPath = params.fontPath || '/static/fonts/ZapfChanceryC.otf'; const cameraNear = .2; @@ -33,22 +36,21 @@ class MazeObject { }; let o = {}; - let mazeGeometries = [], areas = []; + let areas = [], mazeMeshes = []; this.mazeObject = function(def, room, step = 0) { let offsetZ = 0, e; def.len = def.len || 0; if (step == 0) { - e = o.door.geometry.clone(); + e = o.door.clone(); e.rotateY(_tf.rotation.f); - mazeGeometries.push(e); + mazeMeshes.push(e); } for (let i = 0; i < def.len; i++) { - let t = o.tunnel.geometry.clone(); - t.translate(0, 0, i * context.tubeSize + context.wallDepth / 2); + let t = o.tunnel.clone(); + t.position.set(0, 0, i * context.tubeSize + context.wallDepth / 2); def.matrix && t.applyMatrix4(def.matrix); - - mazeGeometries.push(t); + mazeMeshes.push(t); } offsetZ = context.wallDepth + def.len * context.tubeSize - context.tubeSize / 2; if (!def.len) offsetZ = -.275; @@ -58,6 +60,14 @@ class MazeObject { room.localToWorld(new Vector3(context.tubeSize / 2 - cameraNear, 0, offsetZ + cameraNear)) ] }); + + engine.phy.add( + {position: room.localToWorld(new Vector3(context.tubeSize / 2 - cameraNear, 0, offsetZ + cameraNear))}, + 'fixed', false, undefined, 'cuboid',{ + width:offsetZ*10, height:10, depth:10 + } + ) + if (def.type == 'area') { def.area.forEach(ar => { areas.push({ @@ -75,23 +85,23 @@ class MazeObject { // def.matrix && e.applyMatrix4(def.matrix); // mazeGeometries.push(e); } else { - e = [o.floor.geometry.clone(), o.door.geometry.clone(), o[def.r ? 'door' : 'wall'].geometry.clone(), - o[def.f ? 'door' : 'wall'].geometry.clone(), o[def.l ? 'door' : 'wall'].geometry.clone()]; - e[0].translate(0, 0, offsetZ + context.wallSize); + e = [o.floor.clone(), o.door.clone(), o[def.r ? 'door' : 'wall'].clone(), + o[def.f ? 'door' : 'wall'].clone(), o[def.l ? 'door' : 'wall'].clone()]; + e[0].position.set(0, 0, offsetZ + context.wallSize); e[1].rotateY(_tf.rotation.b); e[2].rotateY(_tf.rotation.r); e[3].rotateY(_tf.rotation.f); e[4].rotateY(_tf.rotation.l); - e[1].translate(0, 0, offsetZ + 0); - e[2].translate(-context.wallSize, 0, offsetZ + context.wallSize); - e[3].translate(0, 0, offsetZ + context.wallSize * 2); - e[4].translate(context.wallSize, 0, offsetZ + context.wallSize); + e[1].position.set(0, 0, offsetZ + 0); + e[2].position.set(-context.wallSize, 0, offsetZ + context.wallSize); + e[3].position.set(0, 0, offsetZ + context.wallSize * 2); + e[4].position.set(context.wallSize, 0, offsetZ + context.wallSize); e.forEach(g => { def.matrix && g.applyMatrix4(def.matrix); - mazeGeometries.push(g); + mazeMeshes.push(g); }); areas.push({ a: [ @@ -119,7 +129,7 @@ class MazeObject { mtx.makeRotationY(_tf.rotation[d]); mtx.setPosition(_tf.pNext[d][0], 0, _tf.pNext[d][1] + offsetZ); let rr = new Group(); - scene.add(rr); + root.add(rr); def[d].matrix = mtx.premultiply(def.matrix || new Matrix4()); rr.applyMatrix4(def[d].matrix); rr.updateMatrixWorld(); @@ -132,14 +142,18 @@ class MazeObject { ['tunnel', 'wall', 'door', 'floor'].forEach(e => { o[e] = mazeAsset.scene.getObjectByName(e); o[e].frustumCulled = false; + o[e].scale.set(scale, scale, scale) }); - o.tunnel.material.depthWrite = false this.mazeObject(def, room); - mazeGeometries.forEach(mg=>{ - scene.add(new Mesh(mg, o.tunnel.material)); + mazeMeshes.forEach(mesh=>{ + //let mesh = new Mesh(mg, o.tunnel.material) + root.add(mesh); + engine.phy.add(mesh, 'dynamic') }) + + console.log(o.tunnel) //scene.add(new Mesh(BufferGeometryUtils.mergeGeometries(mazeGeometries, false), o.tunnel.material)); - console.log(room); + //console.log(room); } } } diff --git a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js index 718b608..f9c6d24 100644 --- a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js +++ b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js @@ -23,7 +23,7 @@ class MazeQuizGame { len, objects:[ { - type: 'text', text: cq.s, position:[0,.4,len], rotation:[0,Math.PI, 0] + type: 'text', text: cq.s, position:[0,.44,len-0.2], rotation:[0,Math.PI, 0] } ], [lrv?'r':'l']:{ @@ -32,7 +32,7 @@ class MazeQuizGame { len: lr, objects:[ { - type: 'text', text: cq.h, position:[0,.4,lr], rotation:[0,Math.PI, 0] + type: 'text', text: cq.h, position:[0,.44,lr-0.2], rotation:[0,Math.PI, 0] } ] } diff --git a/src/lib/Clickable.js b/src/lib/Clickable.js index 61db150..ac50ca4 100644 --- a/src/lib/Clickable.js +++ b/src/lib/Clickable.js @@ -20,10 +20,8 @@ class Clickable { raycaster.setFromCamera(mouse, camera); let forExecute = []; objects.forEach(o => { - console.log(1) o.getWorldPosition(v); if (camera.position.distanceTo(v) <= o._clickable.distance && o.visible) { - console.log(2) const intersects = raycaster.intersectObject(o); // intersects.forEach(i=>{ // forExecute.push({o, i}) diff --git a/src/lib/Hero.js b/src/lib/Hero.js index 99b9c07..1b0a8f0 100644 --- a/src/lib/Hero.js +++ b/src/lib/Hero.js @@ -63,22 +63,24 @@ class Hero{ // //colliderBody.sleepSpeedLimit = 1.0; // gameEngine.phy.world.addBody(colliderBody) - let bodyDesc = RAPIER.RigidBodyDesc.kinematicPositionBased().setTranslation( - // characterCollider.position.x, - // characterCollider.position.y, - // characterCollider.position.z - -1, 3, 1 - ) - let rigidBody = gameEngine.phy.world.createRigidBody(bodyDesc); - let dynamicCollider = RAPIER.ColliderDesc.ball(0.28); - gameEngine.phy.world.createCollider(dynamicCollider, rigidBody.handle); + let po = gameEngine.phy.add(this.model, 'kinematicPositionBased', false, undefined, 'ball', { radius: 0.28}) + + // let bodyDesc = RAPIER.RigidBodyDesc.kinematicPositionBased().setTranslation( + // // characterCollider.position.x, + // // characterCollider.position.y, + // // characterCollider.position.z + // -1, 3, 1 + // ) + // let rigidBody = gameEngine.phy.world.createRigidBody(bodyDesc); + // let dynamicCollider = RAPIER.ColliderDesc.ball(0.28); + // gameEngine.phy.world.createCollider(dynamicCollider, rigidBody.handle); this.characterControls = new CharacterControls(this.model, this.mixer, this.animationsMap, gameEngine.orbitControls, gameEngine.camera, 'idle', new RAPIER.Ray( { x: 0, y: 0, z: 0 }, { x: 0, y: -1, z: 0} - ), rigidBody, this.pointerControls) + ), po.rigidBody, this.pointerControls) // this.characterCollider = characterCollider; // this.colliderBody = colliderBody; @@ -146,7 +148,7 @@ class Hero{ this.delta += dlt; if (this.delta > 0.016){ this.characterControls.update(this.gameEngine.phy.world, this.delta, pc) - this.gameEngine.phy.world.step() + this.gameEngine.phy.step() this.delta = 0; } } diff --git a/src/lib/Physics.js b/src/lib/Physics.js new file mode 100644 index 0000000..95b0141 --- /dev/null +++ b/src/lib/Physics.js @@ -0,0 +1,104 @@ +import * as RAPIER from '@dimforge/rapier3d' + +// export type PhysicsObject = { +// mesh: THREE.Mesh +// collider: Rapier.Collider +// rigidBody: Rapier.RigidBody +// fn?: Function +// autoAnimate: boolean +// } + +class Physics{ + constructor(){ + + } + + init = async ()=>{ + let gravity = { x: 0.0, y: -9.81, z: 0.0 }; + let world = new RAPIER.World(gravity); + + // Create Ground. + let bodyDesc = RAPIER.RigidBodyDesc.fixed(); + let body = world.createRigidBody(bodyDesc); + let colliderDesc = RAPIER.ColliderDesc.cuboid(100.0, 0.1, 100.0); + world.createCollider(colliderDesc, body.handle); + this.world = world; + this.physicsObjects = []; + return this; + } + + add = (mesh, rigidBodyType, autoAnimate = true, postPhysicsFn, colliderType, colliderSettings) => { + const rigidBodyDesc = RAPIER.RigidBodyDesc[rigidBodyType]() + rigidBodyDesc.setTranslation(mesh.position.x, mesh.position.y, mesh.position.z) + + // * Responsible for collision response + const rigidBody = this.world.createRigidBody(rigidBodyDesc) + + let colliderDesc + + switch (colliderType) { + case 'cuboid': + { + const { width, height, depth } = colliderSettings + colliderDesc = RAPIER.ColliderDesc.cuboid(width, height, depth) + } + break + + case 'ball': + { + const { radius } = colliderSettings + colliderDesc = RAPIER.ColliderDesc.ball(radius) + } + break + + case 'capsule': + { + const { halfHeight, radius } = colliderSettings + colliderDesc = RAPIER.ColliderDesc.capsule(halfHeight, radius) + } + break + + default: + { + colliderDesc = RAPIER.ColliderDesc.trimesh( + mesh.geometry.attributes.position.array, + mesh.geometry.index?.array + ) + } + break + } + + if (!colliderDesc) { + console.error('Collider Mesh Error: convex mesh creation failed.') + } + + // * Responsible for collision detection + const collider = this.world.createCollider(colliderDesc, rigidBody) + + const physicsObject = { mesh, collider, rigidBody, fn: postPhysicsFn, autoAnimate } + + this.physicsObjects.push(physicsObject) + + return physicsObject + } + + step(){ + this.world.step() + + for (let po of this.physicsObjects) { + const autoAnimate = po.autoAnimate + + if (autoAnimate) { + const mesh = po.mesh + const collider = po.collider + mesh.position.copy(collider.translation()) + mesh.quaternion.copy(collider.rotation() ) + } + + const fn = po.fn + fn && fn() + } + } +} + +export { Physics } \ No newline at end of file diff --git a/src/lib/gameEngine.js b/src/lib/gameEngine.js index 4d98db7..0ac10d7 100644 --- a/src/lib/gameEngine.js +++ b/src/lib/gameEngine.js @@ -13,7 +13,7 @@ import { TransformControls } from 'three/addons/controls/TransformControls.js'; import { ARButton } from 'three/addons/webxr/ARButton.js'; import { XRButton } from 'three/addons/webxr/XRButton.js'; import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js'; -import * as RAPIER from '@dimforge/rapier3d' +import { Physics } from './Physics.js'; import { Clickable } from './Clickable.js'; class GameEngine { @@ -161,7 +161,7 @@ class GameEngine { gameEngine.camera.updateProjectionMatrix(); }) - this.initPhysics(); + await this.initPhysics(); if (opts.ar) { renderer.xr.enabled = true; @@ -246,17 +246,17 @@ class GameEngine { this.cameraYaw = yaw; } - initPhysics() { + async initPhysics() { //const world = new CANNON.World() //world.gravity.set(0, -9.82, 0) - let gravity = { x: 0.0, y: -9.81, z: 0.0 }; - let world = new RAPIER.World(gravity); + // let gravity = { x: 0.0, y: -9.81, z: 0.0 }; + // let world = new RAPIER.World(gravity); - // Create Ground. - let bodyDesc = RAPIER.RigidBodyDesc.fixed(); - let body = world.createRigidBody(bodyDesc); - let colliderDesc = RAPIER.ColliderDesc.cuboid(100.0, 0.1, 100.0); - world.createCollider(colliderDesc, body.handle); + // // Create Ground. + // let bodyDesc = RAPIER.RigidBodyDesc.fixed(); + // let body = world.createRigidBody(bodyDesc); + // let colliderDesc = RAPIER.ColliderDesc.cuboid(100.0, 0.1, 100.0); + // world.createCollider(colliderDesc, body.handle); // const groundMaterial = new CANNON.Material('groundMaterial') // const slipperyMaterial = new CANNON.Material('slipperyMaterial') @@ -276,10 +276,9 @@ class GameEngine { // planeBody.addShape(planeShape) // planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2) // world.addBody(planeBody) - this.phy = { - world, - //slipperyMaterial - } + + this.phy = new Physics(); + await this.phy.init(); } handleXrAction(gameEngine, delta) {