Skip to content

load the threejs scene quickly #5

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Binary file added bootup.mp4
Binary file not shown.
25 changes: 25 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,34 @@
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@hyperbeamapi">
<link href="https://hyperbeam.com/images/favicon.png" rel="shortcut icon" type="image/x-icon">
<style>
html, body {
overflow: hidden;
background-color: #ff889b;
}

#threejscontainer {
position: absolute;
top: 0;
left: 0;
background-color: #ff889b;
}

#hbcontainer {
position: fixed;
top: 50%;
left: 50%;
visibility: hidden;
}

#bootup {
visibility: hidden;
}
</style>
</head>
<body>
<a href="https://github.com/hyperbeam/threejs-example" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="z-index:100; fill:#000; color:#ff889b; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<video id="bootup" type="video/mp4" src="./bootup.mp4" controls="false" autoplay="true"></video>
<div id="threejscontainer"></div>
<div id="hbcontainer"></div>
<script type="module" src="/main.js"></script>
Expand Down
211 changes: 106 additions & 105 deletions main.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import "./style.css"
import * as THREE from "three"
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls"
import Hyperbeam from "@hyperbeam/web"

(async () => {
const renderHyperbeam = renderScene()
let embedURL = "" // Running locally and you have an embed URL? Set it here
if (embedURL === "") {
const room = location.pathname.substring(1)
Expand All @@ -18,10 +18,10 @@ import Hyperbeam from "@hyperbeam/web"
}
embedURL = body.url
}
main(embedURL)
setTimeout(renderHyperbeam, 4000, embedURL)
})()

async function main(embedURL) {
function renderScene() {
const scene = new THREE.Scene()
const pointer = new THREE.Vector2()
const raycaster = new THREE.Raycaster()
Expand All @@ -44,10 +44,9 @@ async function main(embedURL) {
// The default aspect ratio of the virtual computer is 16:9
const width = 0.62
const height = width * 9 / 16
const texture = new THREE.Texture()
// force flipY to false, three.js is inconsistent with this behaviour
bootup.play()
let texture = new THREE.VideoTexture(bootup)
texture.flipY = false
texture.generateMipmaps = false

const geometry = new THREE.PlaneBufferGeometry(width, height)
const material = new THREE.MeshBasicMaterial({ map: texture })
Expand All @@ -63,127 +62,129 @@ async function main(embedURL) {
scene.add(checkerboardMesh(1, 15))
plane.add(sound)

const hb = await Hyperbeam(hbcontainer, embedURL, {
frameCb: (frame) => {
if (texture.image === null) {
if (frame.constructor === HTMLVideoElement) {
// hack: three.js internal methods check for .width and .height
// need to set manually for video so that three.js handles it correctly
frame.width = frame.videoWidth
frame.height = frame.videoHeight
}
texture.image = frame
texture.needsUpdate = true
} else {
renderer.copyTextureToTexture(new THREE.Vector2(0, 0), new THREE.Texture(frame), texture)
}
},
audioTrackCb: tryAudio
})

window.addEventListener("resize", onWindowResized)
window.addEventListener("wheel", onWheel)
window.addEventListener("contextmenu", onContextMenu)
window.addEventListener("pointermove", onPointerMove)
window.addEventListener("pointerdown", onPointerDown)
window.addEventListener("pointerup", onPointerUp)

onWindowResized()
function animate() {
window.requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
animate()

function tryAudio(track) {
sound.setMediaStreamSource(new MediaStream([track]))
sound.setRefDistance(0.5)
// The audio context might be waiting on a user gesture
// In that case, we'll call sound.play() in the pointerdown handler
if (listener.context.state === "running") {
sound.play()
async function renderHyperbeam(embedURL) {
texture.dispose()
texture = new THREE.Texture()
texture.flipY = false // force flipY to false, three.js is inconsistent with this behaviour
texture.generateMipmaps = false
material.map = texture

const hb = await Hyperbeam(hbcontainer, embedURL, {
frameCb: (frame) => {
if (texture.image === null) {
if (frame.constructor === HTMLVideoElement) {
// hack: three.js internal methods check for .width and .height
// need to set manually for video so that three.js handles it correctly
frame.width = frame.videoWidth
frame.height = frame.videoHeight
}
texture.image = frame
texture.needsUpdate = true
} else {
renderer.copyTextureToTexture(new THREE.Vector2(0, 0), new THREE.Texture(frame), texture)
}
},
audioTrackCb: tryAudio
})

window.addEventListener("resize", onWindowResized)
window.addEventListener("wheel", onWheel)
window.addEventListener("contextmenu", onContextMenu)
window.addEventListener("pointermove", onPointerMove)
window.addEventListener("pointerdown", onPointerDown)
window.addEventListener("pointerup", onPointerUp)

onWindowResized()

function tryAudio(track) {
sound.setMediaStreamSource(new MediaStream([track]))
sound.setRefDistance(0.5)
// The audio context might be waiting on a user gesture
// In that case, we'll call sound.play() in the pointerdown handler
if (listener.context.state === "running") {
sound.play()
}
}
}

function onWindowResized() {
const w = window.innerWidth
const h = window.innerHeight
renderer.setSize(w, h)
camera.aspect = w / h
camera.updateProjectionMatrix()
}

function getPlaneIntersects() {
raycaster.setFromCamera(pointer, camera)
return raycaster.intersectObject(plane, false)
}
function onWindowResized() {
const w = window.innerWidth
const h = window.innerHeight
renderer.setSize(w, h)
camera.aspect = w / h
camera.updateProjectionMatrix()
}

function onWheel(e) {
if (getPlaneIntersects().length > 0) {
hb.sendEvent({
type: "wheel",
deltaY: e.deltaY,
})
function getPlaneIntersects() {
raycaster.setFromCamera(pointer, camera)
return raycaster.intersectObject(plane, false)
}
}

function onContextMenu(e) {
if (getPlaneIntersects().length > 0) {
e.preventDefault()
function onWheel(e) {
if (getPlaneIntersects().length > 0) {
hb.sendEvent({
type: "wheel",
deltaY: e.deltaY,
})
}
}
}

function handlePointer(e, type) {
pointer.x = (e.clientX / window.innerWidth) * 2 - 1
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1
const intersects = getPlaneIntersects()
if (intersects.length > 0) {
// We disable the OrbitControls when the user's pointer is on the virtual computer
controls.enabled = false
const vector = new THREE.Vector3().copy(intersects[0].point)
plane.worldToLocal(vector)
hb.sendEvent({
type,
x: vector.x / width + 0.5,
y: -vector.y / height + 0.5,
button: e.button
})
} else {
controls.enabled = true
function onContextMenu(e) {
if (getPlaneIntersects().length > 0) {
e.preventDefault()
}
}
}

function onPointerMove(e) {
handlePointer(e, "mousemove")
}
function handlePointer(e, type) {
pointer.x = (e.clientX / window.innerWidth) * 2 - 1
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1
const intersects = getPlaneIntersects()
if (intersects.length > 0) {
// We disable the OrbitControls when the user's pointer is on the virtual computer
controls.enabled = false
const vector = new THREE.Vector3().copy(intersects[0].point)
plane.worldToLocal(vector)
hb.sendEvent({
type,
x: vector.x / width + 0.5,
y: -vector.y / height + 0.5,
button: e.button
})
} else {
controls.enabled = true
}
}

function onPointerDown(e) {
handlePointer(e, "mousedown")
}
function onPointerMove(e) {
handlePointer(e, "mousemove")
}

async function onPointerUp(e) {
handlePointer(e, "mouseup")
// If the audio context was suspended because there were no user gestures,
// resume the audio context now since the user interacted with the page
if (listener.context.state === "suspended") {
await listener.context.resume()
await sound.play()
function onPointerDown(e) {
handlePointer(e, "mousedown")
}
}

function animate() {
window.requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
async function onPointerUp(e) {
handlePointer(e, "mouseup")
if (listener.context.state === "suspended") { // If the audio context was suspended because there were no user gestures,
await listener.context.resume() // resume the audio context now since the user interacted with the page
await sound.play()
}
}
}
return renderHyperbeam
}

function checkerboardMesh(width, segments) {
const geometry = new THREE.PlaneGeometry(width, width, segments, segments).toNonIndexed()
const material = new THREE.MeshBasicMaterial({
vertexColors: true,
transparent: true,
side: THREE.DoubleSide
})
const material = new THREE.MeshBasicMaterial({ vertexColors: true, transparent: true, side: THREE.DoubleSide })
const positionAttribute = geometry.getAttribute("position")
const colors = []
console.log(segments * segments, positionAttribute.count / 6)
for (let i = 0; i < positionAttribute.count; i++) {
colors.push(0, 0, 0, i % 6 === i % 12 ? 1 : 0)
}
Expand Down
16 changes: 0 additions & 16 deletions style.css

This file was deleted.