Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bit media frames physics #6003

Merged
merged 16 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = (() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code definitely feels pretty funky, and I remember having similar feelings the first time similar code was written for original media. I think a lot of it was around getting physics shapes to line up, but I suspect thats all actually just a sympton f the physics code being wrong and then we bend over backwards to accommodate those weird assumptions in the rest of our code. Definitely wroth revisiting when we get around to a cleanup pass on physics.

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;
};
})();
keianhzo marked this conversation as resolved.
Show resolved Hide resolved

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();
keianhzo marked this conversation as resolved.
Show resolved Hide resolved
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