Skip to content

Commit

Permalink
Merge pull request #6003 from mozilla/bit-media-frames-physics
Browse files Browse the repository at this point in the history
Bit media frames physics
  • Loading branch information
keianhzo authored May 11, 2023
2 parents a1db54a + f23dbad commit d707f9d
Show file tree
Hide file tree
Showing 17 changed files with 332 additions and 167 deletions.
8 changes: 7 additions & 1 deletion src/bit-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const MediaFrame = defineComponent({
scale: [Types.f32, 3],
mediaType: Types.ui8,
bounds: [Types.f32, 3],
guide: Types.eid,
preview: Types.eid,
previewingNid: Types.eid
});
Expand Down Expand Up @@ -156,6 +157,9 @@ export const MediaLoader = defineComponent({
});
MediaLoader.src[$isStringType] = true;
export const MediaLoaded = defineComponent();
export const MediaContentBounds = defineComponent({
bounds: [Types.f32, 3]
});

export const SceneRoot = defineComponent();
export const NavMesh = defineComponent();
Expand All @@ -176,7 +180,8 @@ export const MediaPDF = defineComponent({
MediaPDF.map = new Map();

export const MediaVideo = defineComponent({
autoPlay: Types.ui8
autoPlay: Types.ui8,
ratio: Types.f32
});
export const AnimationMixer = defineComponent();
export const NetworkedVideo = defineComponent({
Expand All @@ -199,6 +204,7 @@ AudioEmitter.audios = new Map();
AudioEmitter.params = new Map();
export const AudioSettingsChanged = defineComponent();
export const Deletable = defineComponent();
export const Deleting = defineComponent();
export const EnvironmentSettings = defineComponent();
EnvironmentSettings.map = new Map();

Expand Down
3 changes: 1 addition & 2 deletions src/bit-systems/camera-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
HoveredRemoteRight,
Interacted,
RemoteRight,
Rigidbody,
TextButton
} from "../bit-components";
import { addMedia } from "../utils/media-utils";
Expand Down Expand Up @@ -212,7 +211,7 @@ function rotateWithRightClick(world, camera) {
userinput.get(paths.device.mouse.buttonRight)
) {
const rightCursor = anyEntityWith(world, RemoteRight);
physicsSystem.updateBodyOptions(Rigidbody.bodyId[camera], { type: "kinematic" });
physicsSystem.updateRigidBodyOptions(camera, { type: "kinematic" });
transformSystem.startTransform(world.eid2obj.get(camera), world.eid2obj.get(rightCursor), {
mode: "cursor"
});
Expand Down
5 changes: 3 additions & 2 deletions src/bit-systems/delete-entity-system.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineQuery, exitQuery, hasComponent, removeEntity } from "bitecs";
import { addComponent, defineQuery, exitQuery, hasComponent, removeEntity } from "bitecs";
import { Vector3 } from "three";
import { HubsWorld } from "../app";
import { Deletable, HoveredRemoteLeft, HoveredRemoteRight } from "../bit-components";
import { Deletable, Deleting, HoveredRemoteLeft, HoveredRemoteRight } from "../bit-components";
import { paths } from "../systems/userinput/paths";
import { animate } from "../utils/animate";
import { findAncestorEntity } from "../utils/bit-utils";
Expand All @@ -18,6 +18,7 @@ function* animateThenRemoveEntity(world: HubsWorld, eid: number): Coroutine {
if (hasSavedEntityState(world, eid)) {
deleteEntityState(APP.hubChannel!, world, eid);
}
addComponent(world, Deleting, eid);
const obj = world.eid2obj.get(eid)!;
yield* animate({
properties: [[obj.scale.clone(), END_SCALE]],
Expand Down
58 changes: 52 additions & 6 deletions src/bit-systems/media-loading.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { addComponent, defineQuery, enterQuery, exitQuery, hasComponent, removeComponent, removeEntity } from "bitecs";
import { Vector3 } from "three";
import { Box3, Euler, Vector3 } from "three";
import { HubsWorld } from "../app";
import { GLTFModel, MediaLoaded, MediaLoader, Networked, ObjectMenuTarget } from "../bit-components";
import {
GLTFModel,
MediaContentBounds,
MediaLoaded,
MediaLoader,
Networked,
ObjectMenuTarget
} from "../bit-components";
import { inflatePhysicsShape, Shape } from "../inflators/physics-shape";
import { ErrorObject } from "../prefabs/error-object";
import { LoadingObject } from "../prefabs/loading-object";
import { animate } from "../utils/animate";
import { setNetworkedDataWithoutRoot } from "../utils/assign-network-ids";
import { computeObjectAABB } from "../utils/auto-box-collider";
import { crClearTimeout, crNextFrame, crTimeout } from "../utils/coroutine";
import { ClearFunction, JobRunner, withRollback } from "../utils/coroutine-utils";
import { easeOutQuadratic } from "../utils/easing";
Expand All @@ -19,6 +27,37 @@ import { loadAudio } from "../utils/load-audio";
import { MediaType, mediaTypeName, resolveMediaInfo } from "../utils/media-utils";
import { EntityID } from "../utils/networking-types";

const getBox = (() => {
const rotation = new Euler();
return (world: HubsWorld, eid: EntityID, rootEid: EntityID, worldSpace?: boolean) => {
const box = new Box3();
const obj = world.eid2obj.get(eid)!;
const rootObj = world.eid2obj.get(rootEid)!;

rotation.copy(obj.rotation);
obj.rotation.set(0, 0, 0);
obj.updateMatrices(true, true);
rootObj.updateMatrices(true, true);
rootObj.updateMatrixWorld(true);

computeObjectAABB(rootObj, box, false);

if (!box.isEmpty()) {
if (!worldSpace) {
obj.worldToLocal(box.min);
obj.worldToLocal(box.max);
}
obj.rotation.copy(rotation);
obj.matrixNeedsUpdate = true;
}

rootObj.matrixWorldNeedsUpdate = true;
rootObj.updateMatrices();

return box;
};
})();

export function* waitForMediaLoaded(world: HubsWorld, eid: EntityID) {
while (hasComponent(world, MediaLoader, eid)) {
yield crNextFrame();
Expand Down Expand Up @@ -55,12 +94,12 @@ function resizeAndRecenter(world: HubsWorld, media: EntityID, eid: EntityID) {
if (!resize && !recenter) return;

const mediaObj = world.eid2obj.get(media)!;
const box = new THREE.Box3();
const box = new Box3();
box.setFromObject(mediaObj);

let scalar = 1;
if (resize) {
const size = new THREE.Vector3();
const size = new Vector3();
box.getSize(size);
scalar = 1 / Math.max(size.x, size.y, size.z);
if (hasComponent(world, GLTFModel, media)) scalar = scalar * 0.5;
Expand All @@ -69,7 +108,7 @@ function resizeAndRecenter(world: HubsWorld, media: EntityID, eid: EntityID) {
}

if (recenter) {
const center = new THREE.Vector3();
const center = new Vector3();
box.getCenter(center);
mediaObj.position.copy(center).multiplyScalar(-1 * scalar);
mediaObj.matrixNeedsUpdate = true;
Expand Down Expand Up @@ -160,6 +199,7 @@ function* loadMedia(world: HubsWorld, eid: EntityID) {
return media;
}

const tmpVector = new Vector3();
function* loadAndAnimateMedia(world: HubsWorld, eid: EntityID, clearRollbacks: ClearFunction) {
if (MediaLoader.flags[eid] & MEDIA_LOADER_FLAGS.IS_OBJECT_MENU_TARGET) {
addComponent(world, ObjectMenuTarget, eid);
Expand All @@ -176,8 +216,14 @@ function* loadAndAnimateMedia(world: HubsWorld, eid: EntityID, clearRollbacks: C
removeComponent(world, MediaLoader, eid);

if (media) {
if (hasComponent(world, MediaLoaded, media)) {
const box = getBox(world, eid, media);
addComponent(world, MediaContentBounds, eid);
box.getSize(tmpVector);
MediaContentBounds.bounds[eid].set(tmpVector.toArray());
}
// TODO update scale?
inflatePhysicsShape(world, media, {
inflatePhysicsShape(world, eid, {
type: hasComponent(world, GLTFModel, media) ? Shape.HULL : Shape.BOX,
minHalfExtent: 0.04
});
Expand Down
3 changes: 2 additions & 1 deletion src/components/body-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ AFRAME.registerComponent("body-helper", {

update: function (prevData) {
if (prevData) {
this.system.updateBody(this.uuid, this.data);
const eid = this.el.object3D.eid;
this.system.updateRigidBody(eid, this.data);
}
},

Expand Down
6 changes: 4 additions & 2 deletions src/components/media-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { waitForDOMContentLoaded } from "../utils/async-utils";

import { SHAPE } from "three-ammo/constants";
import { addComponent, entityExists, removeComponent } from "bitecs";
import { MediaLoading } from "../bit-components";
import { MediaContentBounds, MediaLoading } from "../bit-components";

let loadingObject;

Expand Down Expand Up @@ -279,7 +279,9 @@ AFRAME.registerComponent("media-loader", {
}

// TODO this does duplicate work in some cases, but finish() is the only consistent place to do it
this.contentBounds = getBox(this.el, this.el.getObject3D("mesh")).getSize(new THREE.Vector3());
const contentBounds = getBox(this.el, this.el.getObject3D("mesh")).getSize(new THREE.Vector3());
addComponent(APP.world, MediaContentBounds, el.eid);
MediaContentBounds.bounds[el.eid].set(contentBounds.toArray());

el.emit("media-loaded");
if (el.eid && entityExists(APP.world, el.eid)) {
Expand Down
10 changes: 5 additions & 5 deletions src/inflators/media-frame.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { addObject3DComponent } from "../utils/jsx-entity";
import { NetworkedMediaFrame, MediaFrame, Networked } from "../bit-components";
import { addComponent, hasComponent } from "bitecs";
import { addComponent, addEntity, hasComponent } from "bitecs";
import { MediaType } from "../utils/media-utils";
import { COLLISION_LAYERS } from "../constants";
import { Layers } from "../camera-layers";
import { inflateRigidBody, Type } from "./rigid-body";
import { Fit, inflatePhysicsShape, Shape } from "./physics-shape";
import { Mesh, BoxBufferGeometry, ShaderMaterial, Color, DoubleSide, Group } from "three";
import { Mesh, BoxBufferGeometry, ShaderMaterial, Color, DoubleSide } from "three";

const DEFAULTS = {
bounds: { x: 1, y: 1, z: 1 },
Expand Down Expand Up @@ -51,9 +51,8 @@ export function inflateMediaFrame(world, eid, componentProps) {
})
);
guide.layers.set(Layers.CAMERA_LAYER_UI);
// TODO: This is a hack around the physics system addBody call requiring its body to have parent
guide.parent = new Group();
addObject3DComponent(world, eid, guide);
const guideEid = addEntity(world);
addObject3DComponent(world, guideEid, guide);
addComponent(world, MediaFrame, eid, true);
addComponent(world, NetworkedMediaFrame, eid, true);

Expand All @@ -68,6 +67,7 @@ export function inflateMediaFrame(world, eid, componentProps) {
pdf: MediaType.PDF
}[componentProps.mediaType];
MediaFrame.bounds[eid].set([componentProps.bounds.x, componentProps.bounds.y, componentProps.bounds.z]);
MediaFrame.guide[eid] = guideEid;

inflateRigidBody(world, eid, {
type: Type.KINEMATIC,
Expand Down
43 changes: 26 additions & 17 deletions src/inflators/rigid-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export enum ActivationState {
DISABLE_SIMULATION = 4
}

export type RigiBodyParams = {
export type RigidBodyParams = {
type: Type;
mass: number;
gravity: [number, number, number];
Expand Down Expand Up @@ -88,25 +88,34 @@ export const getBodyFromRigidBody = (eid: number) => {
};
};

export function inflateRigidBody(world: HubsWorld, eid: number, params: Partial<RigiBodyParams>) {
const updateRigidBody = (eid: number, params: RigidBodyParams) => {
Rigidbody.type[eid] = params.type;
Rigidbody.mass[eid] = params.mass;
Rigidbody.gravity[eid].set(params.gravity);
Rigidbody.linearDamping[eid] = params.linearDamping;
Rigidbody.angularDamping[eid] = params.angularDamping;
Rigidbody.linearSleepingThreshold[eid] = params.linearSleepingThreshold;
Rigidbody.angularSleepingThreshold[eid] = params.angularSleepingThreshold;
Rigidbody.angularFactor[eid].set(params.angularFactor);
Rigidbody.activationState[eid] = params.activationState;
params.emitCollisionEvents && (Rigidbody.flags[eid] |= RIGID_BODY_FLAGS.EMIT_COLLISION_EVENTS);
params.disableCollision && (Rigidbody.flags[eid] |= RIGID_BODY_FLAGS.DISABLE_COLLISION);
Rigidbody.collisionFilterGroup[eid] = params.collisionGroup;
Rigidbody.collisionFilterMask[eid] = params.collisionMask;
params.scaleAutoUpdate && (Rigidbody.flags[eid] |= RIGID_BODY_FLAGS.SCALE_AUTO_UPDATE);
};

export const updateRigiBodyParams = (eid: number, params: Partial<RigidBodyParams>) => {
const currentParams = getBodyFromRigidBody(eid);
const bodyParams = Object.assign({}, currentParams, params);
updateRigidBody(eid, bodyParams);
};

export function inflateRigidBody(world: HubsWorld, eid: number, params: Partial<RigidBodyParams>) {
const bodyParams = Object.assign({}, DEFAULTS, params);

addComponent(world, Rigidbody, eid);

Rigidbody.type[eid] = bodyParams.type;
Rigidbody.mass[eid] = bodyParams.mass;
Rigidbody.gravity[eid].set(bodyParams.gravity);
Rigidbody.linearDamping[eid] = bodyParams.linearDamping;
Rigidbody.angularDamping[eid] = bodyParams.angularDamping;
Rigidbody.linearSleepingThreshold[eid] = bodyParams.linearSleepingThreshold;
Rigidbody.angularSleepingThreshold[eid] = bodyParams.angularSleepingThreshold;
Rigidbody.angularFactor[eid].set(bodyParams.angularFactor);
Rigidbody.activationState[eid] = bodyParams.activationState;
params.emitCollisionEvents && (Rigidbody.flags[eid] |= RIGID_BODY_FLAGS.EMIT_COLLISION_EVENTS);
params.disableCollision && (Rigidbody.flags[eid] |= RIGID_BODY_FLAGS.DISABLE_COLLISION);
Rigidbody.collisionFilterGroup[eid] = bodyParams.collisionGroup;
Rigidbody.collisionFilterMask[eid] = bodyParams.collisionMask;
bodyParams.scaleAutoUpdate && (Rigidbody.flags[eid] |= RIGID_BODY_FLAGS.SCALE_AUTO_UPDATE);
updateRigidBody(eid, bodyParams);

return eid;
}
1 change: 1 addition & 0 deletions src/inflators/video.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export function inflateVideo(world, eid, { texture, ratio, projection, autoPlay
addObject3DComponent(world, eid, mesh);
addComponent(world, MediaVideo, eid);
MediaVideo.autoPlay[eid] = autoPlay ? 1 : 0;
MediaVideo.ratio[eid] = ratio;
return eid;
}
4 changes: 2 additions & 2 deletions src/systems/bit-constraints-system.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function add(world, physicsSystem, interactor, constraintComponent, entities) {
for (let i = 0; i < entities.length; i++) {
const eid = findAncestorEntity(world, entities[i], ancestor => hasComponent(world, Rigidbody, ancestor));
takeOwnership(world, eid);
physicsSystem.updateBodyOptions(Rigidbody.bodyId[eid], grabBodyOptions);
physicsSystem.updateRigidBodyOptions(eid, grabBodyOptions);
physicsSystem.addConstraint(interactor, Rigidbody.bodyId[eid], Rigidbody.bodyId[interactor], {});
addComponent(world, Constraint, eid);
addComponent(world, constraintComponent, eid);
Expand All @@ -58,7 +58,7 @@ function remove(world, offersConstraint, constraintComponent, physicsSystem, int
const eid = findAncestorEntity(world, entities[i], ancestor => hasComponent(world, Rigidbody, ancestor));
if (!entityExists(world, eid)) continue;
if (hasComponent(world, offersConstraint, entities[i]) && hasComponent(world, Rigidbody, eid)) {
physicsSystem.updateBodyOptions(Rigidbody.bodyId[eid], releaseBodyOptions);
physicsSystem.updateRigidBodyOptions(eid, releaseBodyOptions);
physicsSystem.removeConstraint(interactor);
removeComponent(world, constraintComponent, eid);
if (
Expand Down
Loading

0 comments on commit d707f9d

Please sign in to comment.