-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Add basic support for loading media with bitecs behind feature flag. #5677
Merged
Merged
Changes from all commits
Commits
Show all changes
85 commits
Select commit
Hold shift + click to select a range
4077884
[Work in progress] Load images without AFRAME
johnshaughnessy 7885b32
Stub out video
johnshaughnessy 87b0ef6
Add models to media-loading system
johnshaughnessy e95c2e7
Resize and recenter media after it loads
johnshaughnessy 12e82d8
Add chain for sequential coroutines.
johnshaughnessy c07ccf4
Animate the proxy object
johnshaughnessy b7e9a9f
Start loading videos
johnshaughnessy b47b18b
Simplify coroutines and cancelables
johnshaughnessy 3da1980
Use AbortControllers and AbortSignals
johnshaughnessy 1e831b2
Merge remote-tracking branch 'origin/master' into feature/load-media
johnshaughnessy d5c6e95
Fix error handling in coroutine / cancelable
johnshaughnessy 77554f8
Merge remote-tracking branch 'origin/add-typescript' into feature/loa…
johnshaughnessy cd6962f
Add networked video playback state
johnshaughnessy 73af100
Separate the menus from the video entity
johnshaughnessy 9a92bd8
Fix cursor hovering on video menu
johnshaughnessy 8fc2978
Formatting
johnshaughnessy 0346a37
Remove unused code
johnshaughnessy 7df8801
Add type stub for troika-three-text
netpro2k 9d94e7a
Convert jsx-entities to ts, type JSX better
netpro2k db63f2f
Minor type fixes
netpro2k 1a98179
Fix taking ownership of loaded media
johnshaughnessy a75023e
Remove NOCOMMIT
johnshaughnessy 889fefc
Add seek-slider to video
johnshaughnessy 4392a9f
Update play/pause button label
johnshaughnessy c9e485d
Fix hovering on playhead
johnshaughnessy 4816d40
Minor tweaks to video menu layout
netpro2k 99f2a49
Intersecting In The Plane Of!
johnshaughnessy 42d6e14
Basic audio playback for videos
netpro2k 64965b9
Fix minor issue where time label is one frame late
johnshaughnessy 91d3181
Rename mediaVideoSystem -> videoSystem
johnshaughnessy e920990
Toggle video play/pause on click. Grab to move.
johnshaughnessy 91c620c
Fix action set toggle: Hands do not hover on videos
johnshaughnessy 3a11b77
Add deleteEntitySystem
johnshaughnessy b85e4a9
Add animation to entity deletion
johnshaughnessy c5bb832
Add transparent play/pause indicators
johnshaughnessy caaa4b9
Merge branch 'add-typescript' into feature/load-media
netpro2k e9d552a
Fix asset imports by using babel-loader for TS
netpro2k 4173296
Convert dash-case inflators to camelCase
netpro2k 3615803
Support pasting files
johnshaughnessy 8a2948f
Handle dropping files
johnshaughnessy ec02ce0
Add newloader query string param as feature flag
johnshaughnessy 3c44d34
Fix classic video menus / interactions
johnshaughnessy 08c8cf9
Update todo
johnshaughnessy dae8bf9
Remove todo file
johnshaughnessy 6c5825a
Delete unused file
johnshaughnessy fd2ad95
Remove unused imports
johnshaughnessy 8ce2940
Remove MediaLoader component after load
johnshaughnessy f8153d2
Undo change to cursor controller
johnshaughnessy 98c2434
Fix grabbing videos
johnshaughnessy 6c9d132
Remove unused animation mixer system
johnshaughnessy 8a7c711
Add Constraint components for floaty-object
johnshaughnessy c60191d
Move utils/onpaste to load-media-on-paste-or-drop
johnshaughnessy 80569f5
Add TextureCacheKey component.
johnshaughnessy df99c68
Warn on unrecognized component.
johnshaughnessy eec20d2
Fix button props
johnshaughnessy 2c291b3
Use MediaParams in inflateMediaLoader
johnshaughnessy 6f9f96b
Throw error if url parsing fails
johnshaughnessy 11fcd85
Simplify function parameters
johnshaughnessy b5b81a6
Consolidate MEDIA_TYPE and MediaType
johnshaughnessy 5e695e9
Simplify loader function signatures
johnshaughnessy 157b0ea
Simplify loadVideoTexture parameters
johnshaughnessy b89f1e7
Remove unnecessary workaround
johnshaughnessy 4edc977
Remove log
johnshaughnessy fc4b58c
Simplify method signatures
johnshaughnessy f3c31d2
Simplify function parameters
johnshaughnessy a35e2d1
Simplify function parameters
johnshaughnessy 10680ea
Remove unused code
johnshaughnessy 7a73e21
Use AnimationConfig type for calls to animate
johnshaughnessy e21a247
Remove unused code
johnshaughnessy 9a4252e
Remove unnecessary workaround
johnshaughnessy b32448c
Avoid extra copy
johnshaughnessy 8b1f2ce
Preload assets. Dry up model loading
johnshaughnessy c89a192
Rename MediaParams -> MediaLoaderParams
johnshaughnessy 359cad5
Rename fetchUrlData -> resolveMediaInfo
johnshaughnessy c526275
Restore dontHoverAndHold.
johnshaughnessy d54c184
rename
johnshaughnessy df37251
Add ECS Debug sidebar on ecsDebug qs param
johnshaughnessy dbeeea6
Remove duplicate exclude block
netpro2k bea7e3f
delint
netpro2k a83deb6
Rename couroutine timer functions
netpro2k 889a9c7
Fix video menu position updating issues
netpro2k 8a89e6f
Merge remote-tracking branch 'origin/master' into feature/load-media
netpro2k a45e692
Fix ECS Debug button label
netpro2k 59c7513
Clean up loading cube removal
netpro2k b92010f
Delint
netpro2k File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { defineQuery, exitQuery, hasComponent, removeEntity } from "bitecs"; | ||
import { Vector3 } from "three"; | ||
import { HubsWorld } from "../app"; | ||
import { Deletable, HoveredRemoteLeft, HoveredRemoteRight } from "../bit-components"; | ||
import { paths } from "../systems/userinput/paths"; | ||
import { animate } from "../utils/animate"; | ||
import { findAncestorEntity } from "../utils/bit-utils"; | ||
import { coroutine } from "../utils/coroutine"; | ||
import { easeOutQuadratic } from "../utils/easing"; | ||
|
||
// TODO Move to coroutine.ts when it exists | ||
// TODO Figure out the appropriate type and use it everywhere | ||
export type Coroutine = Generator<Promise<void>, void, unknown>; | ||
|
||
const END_SCALE = new Vector3().setScalar(0.001); | ||
function* animateThenRemoveEntity(world: HubsWorld, eid: number): Coroutine { | ||
const obj = world.eid2obj.get(eid)!; | ||
yield* animate({ | ||
properties: [[obj.scale.clone(), END_SCALE]], | ||
durationMS: 400, | ||
easing: easeOutQuadratic, | ||
fn: ([scale]: [Vector3]) => { | ||
obj.scale.copy(scale); | ||
obj.matrixNeedsUpdate = true; | ||
} | ||
}); | ||
removeEntity(world, eid); | ||
} | ||
|
||
const deletableQuery = defineQuery([Deletable]); | ||
const deletableExitQuery = exitQuery(deletableQuery); | ||
const hoveredRightQuery = defineQuery([HoveredRemoteRight]); | ||
const hoveredLeftQuery = defineQuery([HoveredRemoteLeft]); | ||
const coroutines = new Map(); | ||
|
||
function deleteTheDeletableAncestor(world: HubsWorld, eid: number) { | ||
const ancestor = findAncestorEntity(world, eid, (e: number) => hasComponent(world, Deletable, e)); | ||
if (ancestor && !coroutines.has(ancestor)) { | ||
coroutines.set(ancestor, coroutine(animateThenRemoveEntity(world, ancestor))); | ||
} | ||
} | ||
|
||
export function deleteEntitySystem(world: HubsWorld, userinput: any) { | ||
deletableExitQuery(world).forEach(function (eid) { | ||
coroutines.delete(eid); | ||
}); | ||
if (userinput.get(paths.actions.cursor.right.deleteEntity)) { | ||
hoveredRightQuery(world).forEach(eid => deleteTheDeletableAncestor(world, eid)); | ||
netpro2k marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
if (userinput.get(paths.actions.cursor.left.deleteEntity)) { | ||
hoveredLeftQuery(world).forEach(eid => deleteTheDeletableAncestor(world, eid)); | ||
} | ||
coroutines.forEach(c => c()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import { LoadingObject } from "../prefabs/loading-object"; | ||
import { ErrorObject } from "../prefabs/error-object"; | ||
import { easeOutQuadratic } from "../utils/easing"; | ||
import { loadImage } from "../utils/load-image"; | ||
import { loadVideo } from "../utils/load-video"; | ||
import { loadModel } from "../utils/load-model"; | ||
import { MediaType, resolveMediaInfo } from "../utils/media-utils"; | ||
import { defineQuery, enterQuery, exitQuery, hasComponent, removeComponent, removeEntity } from "bitecs"; | ||
import { MediaLoader, Networked } from "../bit-components"; | ||
import { crTimeout, crClearTimeout, cancelable, coroutine, makeCancelable } from "../utils/coroutine"; | ||
import { takeOwnership } from "../systems/netcode"; | ||
import { renderAsEntity } from "../utils/jsx-entity"; | ||
import { animate } from "../utils/animate"; | ||
|
||
const loaderForMediaType = { | ||
[MediaType.IMAGE]: (world, { accessibleUrl, contentType }) => loadImage(world, accessibleUrl, contentType), | ||
[MediaType.VIDEO]: (world, { accessibleUrl }) => loadVideo(world, accessibleUrl), | ||
[MediaType.MODEL]: (world, { accessibleUrl }) => loadModel(world, accessibleUrl) | ||
}; | ||
|
||
export const MEDIA_LOADER_FLAGS = { | ||
RECENTER: 1 << 0, | ||
RESIZE: 1 << 1 | ||
}; | ||
|
||
function assignNetworkIds(world, mediaEid, mediaLoaderEid) { | ||
const rootNid = APP.getString(Networked.id[mediaLoaderEid]); | ||
let i = 0; | ||
world.eid2obj.get(mediaEid).traverse(function (obj) { | ||
if (obj.eid && hasComponent(world, Networked, obj.eid)) { | ||
const eid = obj.eid; | ||
Networked.id[eid] = APP.getSid(`${rootNid}.${i}`); | ||
APP.world.nid2eid.set(Networked.id[eid], eid); | ||
Networked.creator[eid] = Networked.creator[mediaLoaderEid]; | ||
Networked.owner[eid] = Networked.owner[mediaLoaderEid]; | ||
if (APP.getSid(NAF.clientId) === Networked.owner[mediaLoaderEid]) takeOwnership(world, eid); | ||
i += 1; | ||
} | ||
}); | ||
} | ||
|
||
function resizeAndRecenter(world, media, eid) { | ||
const resize = MediaLoader.flags[eid] & MEDIA_LOADER_FLAGS.RESIZE; | ||
const recenter = MediaLoader.flags[eid] & MEDIA_LOADER_FLAGS.RECENTER; | ||
if (!resize && !recenter) return; | ||
|
||
const mediaObj = world.eid2obj.get(media); | ||
const box = new THREE.Box3(); | ||
box.setFromObject(mediaObj); | ||
|
||
let scale = 1; | ||
if (resize) { | ||
const size = new THREE.Vector3(); | ||
box.getSize(size); | ||
// Might want to consider avatar scale someday | ||
scale = 0.5 / Math.max(size.x, size.y, size.z); | ||
mediaObj.scale.setScalar(scale); | ||
mediaObj.matrixNeedsUpdate = true; | ||
} | ||
|
||
if (recenter) { | ||
const center = new THREE.Vector3(); | ||
box.getCenter(center); | ||
mediaObj.position.copy(center).multiplyScalar(-1 * scale); | ||
mediaObj.matrixNeedsUpdate = true; | ||
} | ||
} | ||
|
||
function* animateScale(world, media) { | ||
const mediaObj = world.eid2obj.get(media); | ||
|
||
const onAnimate = ([position, scale]) => { | ||
mediaObj.position.copy(position); | ||
mediaObj.scale.copy(scale); | ||
mediaObj.matrixNeedsUpdate = true; | ||
}; | ||
|
||
const startScale = new THREE.Vector3().setScalar(0.0001); | ||
const endScale = new THREE.Vector3().setScalar(mediaObj.scale.x); | ||
|
||
const startPosition = new THREE.Vector3().copy(mediaObj.position).multiplyScalar(startScale.x); | ||
const endPosition = new THREE.Vector3().copy(mediaObj.position); | ||
|
||
// Set the initial position and yield one frame | ||
// because the first frame that we render a new object is slow | ||
// TODO: We could move uploading textures to the GPU to the loader, | ||
// so that we don't hitch here | ||
onAnimate([startPosition, startScale]); | ||
yield Promise.resolve(); | ||
|
||
yield* animate({ | ||
properties: [ | ||
[startPosition, endPosition], | ||
[startScale, endScale] | ||
], | ||
durationMS: 400, | ||
easing: easeOutQuadratic, | ||
fn: onAnimate | ||
}); | ||
} | ||
|
||
function add(world, child, parent) { | ||
const parentObj = world.eid2obj.get(parent); | ||
const childObj = world.eid2obj.get(child); | ||
parentObj.add(childObj); | ||
|
||
// TODO: Fix this in THREE.Object3D.add | ||
childObj.matrixWorldNeedsUpdate = true; | ||
} | ||
|
||
function* loadMedia(world, eid) { | ||
let loadingObjEid = 0; | ||
const addLoadingObjectTimeout = crTimeout(() => { | ||
loadingObjEid = renderAsEntity(world, LoadingObject()); | ||
add(world, loadingObjEid, eid); | ||
}, 400); | ||
yield makeCancelable(() => loadingObjEid && removeEntity(world, loadingObjEid)); | ||
const src = APP.getString(MediaLoader.src[eid]); | ||
let media; | ||
try { | ||
const urlData = yield resolveMediaInfo(src); | ||
media = yield* loaderForMediaType[urlData.mediaType](world, urlData); | ||
} catch (e) { | ||
console.error(e); | ||
media = renderAsEntity(world, ErrorObject()); | ||
} | ||
crClearTimeout(addLoadingObjectTimeout); | ||
loadingObjEid && removeEntity(world, loadingObjEid); | ||
return media; | ||
} | ||
|
||
function* loadAndAnimateMedia(world, eid, signal) { | ||
const { value: media, canceled } = yield* cancelable(loadMedia(world, eid), signal); | ||
if (!canceled) { | ||
assignNetworkIds(world, media, eid); | ||
resizeAndRecenter(world, media, eid); | ||
add(world, media, eid); | ||
yield* animateScale(world, media); | ||
removeComponent(world, MediaLoader, eid); | ||
} | ||
} | ||
|
||
const jobs = new Set(); | ||
const abortControllers = new Map(); | ||
const mediaLoaderQuery = defineQuery([MediaLoader]); | ||
const mediaLoaderEnterQuery = enterQuery(mediaLoaderQuery); | ||
const mediaLoaderExitQuery = exitQuery(mediaLoaderQuery); | ||
export function mediaLoadingSystem(world) { | ||
mediaLoaderEnterQuery(world).forEach(function (eid) { | ||
const ac = new AbortController(); | ||
abortControllers.set(eid, ac); | ||
jobs.add(coroutine(loadAndAnimateMedia(world, eid, ac.signal))); | ||
}); | ||
|
||
mediaLoaderExitQuery(world).forEach(function (eid) { | ||
const ac = abortControllers.get(eid); | ||
ac.abort(); | ||
abortControllers.delete(eid); | ||
}); | ||
|
||
jobs.forEach(c => { | ||
if (c().done) { | ||
jobs.delete(c); | ||
} | ||
}); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, what to call
Deletable
.Removable
is equally bad..RemovableRoot
,UserRemovable
? Not very good either..