add physics to interactive objects
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
|
||||
<script>
|
||||
import { GameEngine } from '@/lib/GameEngine.js';
|
||||
import { autoScale } from '@/lib/MeshUtils';
|
||||
let gameEngine = null;
|
||||
|
||||
export default{
|
||||
@@ -75,7 +76,7 @@ export default{
|
||||
this.animations = gltf.animations.map(a => ({
|
||||
name: a.name, id: a.uuid
|
||||
}));
|
||||
gameEngine.autoScale(gltf.scene);
|
||||
autoScale(gltf.scene);
|
||||
let bb = new gameEngine.$.Box3().setFromObject(gltf.scene);
|
||||
gltf.scene.traverse(function (o) {
|
||||
o.frustumCulled = false;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxMaxLength } from "@/lib/MeshUtils";
|
||||
import { getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxMaxLength, centerOrigin } from "@/lib/MeshUtils";
|
||||
|
||||
class GenericObject{
|
||||
constructor(engine, data){
|
||||
return new Promise(async(resolve, reject)=>{
|
||||
this.source = await engine.load(data.$go.asset.name);
|
||||
this.object = this.source.scene;
|
||||
this.object = centerOrigin(this.source.scene)
|
||||
|
||||
if (!data.exclude){
|
||||
engine.clickable.add(this.object, async e=>{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<v-textarea :label="l.description" v-model="modelValue.description"></v-textarea>
|
||||
<v-checkbox density="compact" v-model="modelValue.hud" hide-details label="Observe in head-up display"></v-checkbox>
|
||||
<v-checkbox density="compact" v-model="modelValue.exclude" hide-details label="Disable interactions"></v-checkbox>
|
||||
<v-checkbox density="compact" v-model="modelValue.noPhysics" hide-details label="Disable physics"></v-checkbox>
|
||||
<v-img :src="`/asset/thumb/${modelValue.go}.webp`" />
|
||||
<div class="text-caption text-center">{{ modelValue.title }}</div>
|
||||
</div>
|
||||
|
||||
@@ -41,6 +41,7 @@ export default {
|
||||
props:['modelValue'],
|
||||
mounted(){
|
||||
this.modelValue.questions ??= [];
|
||||
this.modelValue.noPhysics = true;
|
||||
},
|
||||
methods:{
|
||||
addQuestion(){
|
||||
|
||||
@@ -43,6 +43,7 @@ export default {
|
||||
this.modelValue.count = 1000;
|
||||
this.modelValue.w = 50;
|
||||
this.modelValue.h = 50;
|
||||
this.modelValue.noPhysics = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three';
|
||||
import { MotionEngine } from '../../lib/MotionEngine';
|
||||
import { centerOrigin } from '@/lib/MeshUtils';
|
||||
|
||||
class PuzzleGame1 {
|
||||
constructor(engine, data) {
|
||||
return new Promise(async (resolve, reject)=>{
|
||||
let w = data.w, h = data.h, wh = w*h;
|
||||
this.object = new Group();
|
||||
let container = new Group();
|
||||
const aq = new MotionEngine();
|
||||
const pr = [[0, 2], [0, -1], [0, 1], [1, 0], [-1, 0], [0, 0]];
|
||||
let d = 1.2;
|
||||
@@ -50,16 +51,16 @@ class PuzzleGame1 {
|
||||
if (idxs[4] == idxs[5]){
|
||||
mesh._dd = true;
|
||||
}
|
||||
this.object.add(mesh);
|
||||
container.add(mesh);
|
||||
}
|
||||
|
||||
this.object.children[0].onBeforeRender = () => {
|
||||
container.children[0].onBeforeRender = () => {
|
||||
this.update();
|
||||
};
|
||||
|
||||
var check = () => {
|
||||
let i = 0;
|
||||
this.object.children.forEach(o=>{
|
||||
container.children.forEach(o=>{
|
||||
if (o._ri == 5 || o._dd && o._ri == 0) i++;
|
||||
})
|
||||
return i == wh;
|
||||
@@ -76,7 +77,7 @@ class PuzzleGame1 {
|
||||
}
|
||||
};
|
||||
|
||||
this.object.children.forEach(c => {
|
||||
container.children.forEach(c => {
|
||||
engine.clickable.add(c, clickFn);
|
||||
});
|
||||
|
||||
@@ -84,18 +85,20 @@ class PuzzleGame1 {
|
||||
aq.update();
|
||||
if (aq.isIdle() && !this.done && check()) {
|
||||
this.done = true;
|
||||
this.object.children.forEach((c, i) => {
|
||||
container.children.forEach((c, i) => {
|
||||
aq.add({
|
||||
o: c,
|
||||
a: { position: { x: i % w, y: Math.trunc(i/w)} },
|
||||
t: 1,
|
||||
f: i == 0 && this.onfinish
|
||||
f: i == 0 && this.onfinish || undefined
|
||||
});
|
||||
});
|
||||
//engine.dashboard.addPoints(10);
|
||||
}
|
||||
};
|
||||
|
||||
this.object = centerOrigin(container);
|
||||
|
||||
resolve(this);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three';
|
||||
import { MotionEngine } from '../../lib/MotionEngine';
|
||||
import { centerOrigin } from '@/lib/MeshUtils';
|
||||
|
||||
class PuzzleGame2 {
|
||||
constructor(engine, data) {
|
||||
@@ -17,7 +18,7 @@ class PuzzleGame2 {
|
||||
});
|
||||
let last, lidx = w - 1;
|
||||
|
||||
this.object = new Group();
|
||||
let container = new Group();
|
||||
let d = 1.2, p = [];
|
||||
|
||||
function check() {
|
||||
@@ -60,7 +61,7 @@ class PuzzleGame2 {
|
||||
// }
|
||||
p.forEach((e, i) => {
|
||||
let x = e % w, y = ~~(e / h);
|
||||
this.object.children[i].position.set(x * d, y * d, 0);
|
||||
container.children[i].position.set(x * d, y * d, 0);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -89,11 +90,11 @@ class PuzzleGame2 {
|
||||
}
|
||||
let mesh = new Mesh(bg, i != lidx ? material : m2);
|
||||
mesh.position.set(x * d, y * d, 0);
|
||||
this.object.add(mesh);
|
||||
container.add(mesh);
|
||||
}
|
||||
last = this.object.children[lidx];
|
||||
last = container.children[lidx];
|
||||
|
||||
this.object.children[0].onBeforeRender = () => {
|
||||
container.children[0].onBeforeRender = () => {
|
||||
this.update();
|
||||
};
|
||||
|
||||
@@ -101,7 +102,7 @@ class PuzzleGame2 {
|
||||
|
||||
let clickFn = (i) => {
|
||||
if (!this.done && !aq.isActive(i.object)) {
|
||||
let idx = this.object.children.indexOf(i.object);
|
||||
let idx = container.children.indexOf(i.object);
|
||||
if (idx == lidx) return; //we ignore the empty cell
|
||||
let xc = p[idx] % w, yc = ~~(p[idx] / h);
|
||||
let xl = p[lidx] % w, yl = ~~(p[lidx] / h);
|
||||
@@ -122,7 +123,7 @@ class PuzzleGame2 {
|
||||
}
|
||||
};
|
||||
|
||||
this.object.children.forEach(c => {
|
||||
container.children.forEach(c => {
|
||||
engine.clickable.add(c, clickFn);
|
||||
});
|
||||
|
||||
@@ -130,7 +131,7 @@ class PuzzleGame2 {
|
||||
aq.update();
|
||||
if (aq.isIdle() && !this.done && check()) {
|
||||
this.done = true;
|
||||
this.object.children.forEach((c, i) => {
|
||||
container.children.forEach((c, i) => {
|
||||
last.material = material;
|
||||
aq.add({
|
||||
o: c,
|
||||
@@ -142,6 +143,7 @@ class PuzzleGame2 {
|
||||
//engine.dashboard.addPoints(10);
|
||||
}
|
||||
};
|
||||
this.object = centerOrigin(container)
|
||||
resolve(this)
|
||||
});
|
||||
}
|
||||
|
||||
+24
-6
@@ -1,4 +1,4 @@
|
||||
import { TextureLoader, Box3, Vector3 } from "three";
|
||||
import { TextureLoader, Box3, Vector3, Group } from "three";
|
||||
|
||||
function assignParams(mesh, params){
|
||||
['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
|
||||
@@ -30,16 +30,22 @@ function getBoundingBox(object){
|
||||
return new Box3().setFromObject(object);
|
||||
}
|
||||
|
||||
function getBoundingBoxSize(bb){
|
||||
return new Vector3(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
|
||||
}
|
||||
|
||||
function getBoundingBoxMaxLength(bb){
|
||||
return Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z)
|
||||
let size = getBoundingBoxSize(bb)
|
||||
return Math.max(size.x, size.y, size.z)
|
||||
}
|
||||
|
||||
function getBoundingBoxCenterPoint(bb, relativeTo){
|
||||
relativeTo = relativeTo || new Vector3(0,0,0)
|
||||
let size = getBoundingBoxSize(bb)
|
||||
return new Vector3(
|
||||
bb.min.x + (bb.max.x - bb.min.x)/2 - relativeTo.x,
|
||||
bb.min.y + (bb.max.y - bb.min.y)/2 - relativeTo.y,
|
||||
bb.min.z + (bb.max.z - bb.min.z)/2 - relativeTo.z
|
||||
bb.min.x + (size.x)/2 - relativeTo.x,
|
||||
bb.min.y + (size.y)/2 - relativeTo.y,
|
||||
bb.min.z + (size.z)/2 - relativeTo.z
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,4 +55,16 @@ function autoScale(object, mk = 1) {
|
||||
object.scale.multiplyScalar(mk / k);
|
||||
}
|
||||
|
||||
export { assignParams, assignMaterial, autoScale, getBoundingBox, getBoundingBoxMaxLength, getBoundingBoxCenterPoint }
|
||||
function centerOrigin(object){
|
||||
let result = new Group();
|
||||
let bb = getBoundingBox(object);
|
||||
let position = getBoundingBoxCenterPoint(bb, object.position).negate();
|
||||
object.position.copy(position)
|
||||
result.add(object);
|
||||
return result;
|
||||
}
|
||||
|
||||
export {
|
||||
assignParams, assignMaterial, autoScale, centerOrigin,
|
||||
getBoundingBox, getBoundingBoxSize, getBoundingBoxMaxLength, getBoundingBoxCenterPoint
|
||||
}
|
||||
+7
-11
@@ -20,9 +20,10 @@ class Physics{
|
||||
return this;
|
||||
}
|
||||
|
||||
add = (mesh, rigidBodyType, autoAnimate = true, postPhysicsFn, colliderType, colliderSettings) => {
|
||||
add = (mesh, rigidBodyType, autoAnimate = true, postPhysicsFn, colliderType, colliderSettings = {}) => {
|
||||
const rigidBodyDesc = RAPIER.RigidBodyDesc[rigidBodyType]()
|
||||
rigidBodyDesc.setTranslation(mesh.position.x, mesh.position.y, mesh.position.z)
|
||||
mesh.quaternion && rigidBodyDesc.setRotation(mesh.quaternion)
|
||||
|
||||
// * Responsible for collision response
|
||||
const rigidBody = this.world.createRigidBody(rigidBodyDesc)
|
||||
@@ -70,6 +71,7 @@ class Physics{
|
||||
|
||||
// * Responsible for collision detection
|
||||
const collider = this.world.createCollider(colliderDesc, rigidBody)
|
||||
mesh.quaternion && collider.setRotationWrtParent(mesh.quaternion)
|
||||
const physicsObject = { mesh, collider, rigidBody, fn: postPhysicsFn, autoAnimate }
|
||||
this.physicsObjects.push(physicsObject)
|
||||
|
||||
@@ -89,17 +91,11 @@ class Physics{
|
||||
this.world.step(this.eventQueue)
|
||||
|
||||
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() )
|
||||
if (po.autoAnimate) {
|
||||
po.mesh.position.copy(po.rigidBody.translation())
|
||||
po.mesh.quaternion.copy(po.rigidBody.rotation() )
|
||||
}
|
||||
|
||||
const fn = po.fn
|
||||
fn && fn()
|
||||
po.fn?.()
|
||||
}
|
||||
|
||||
this.eventQueue.drainCollisionEvents((handle1, handle2, started) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { InteractiveObject } from '@/components/InteractiveObjects/InteractiveOb
|
||||
import { VideoPlayer } from '@/components/InteractiveObjects/VideoPlayer';
|
||||
import { GameEngine } from '@/lib/GameEngine';
|
||||
import { Hero } from '@/lib/Hero';
|
||||
import { autoScale } from '@/lib/MeshUtils';
|
||||
import { autoScale, getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxSize } from '@/lib/MeshUtils';
|
||||
let gameEngine = null;
|
||||
|
||||
export default {
|
||||
@@ -151,6 +151,14 @@ export default {
|
||||
if (io.source?.animations?.length){
|
||||
gameEngine.playAnimation(gameEngine.scene, io.source.animations[0]);
|
||||
}
|
||||
|
||||
if (!i.data.noPhysics){
|
||||
let bb = getBoundingBox(io.object);
|
||||
let bbs = getBoundingBoxSize(bb);
|
||||
gameEngine.physics.add(io.object, 'fixed', false, undefined, 'capsule', {
|
||||
radius: Math.max(bbs.x, bbs.z)/2, halfHeight: bbs.y/2
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user