forked from bgstaal/multipleWindow3dScene
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1d55eff
Showing
6 changed files
with
368 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
node_modules | ||
build | ||
deploy | ||
screens* | ||
npm-debug.log | ||
*.png | ||
.env | ||
.DS_Store |
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,3 @@ | ||
#3d scene spanning multiple windows using three.js and localStorage | ||
|
||
A simple example showing how to setup a 3d scene across windows on the same origin using three.js and localStorage |
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,153 @@ | ||
class WindowManager | ||
{ | ||
#windows; | ||
#count; | ||
#id; | ||
#winData; | ||
#winShapeChangeCallback; | ||
#winChangeCallback; | ||
|
||
constructor () | ||
{ | ||
let that = this; | ||
|
||
addEventListener("storage", (event) => | ||
{ | ||
//console.log(event) | ||
if (event.key == "windows") | ||
{ | ||
let newWindows = JSON.parse(event.newValue); | ||
let winChange = that.#didWindowsChange(that.#windows, newWindows); | ||
|
||
that.#windows = newWindows; | ||
|
||
if (winChange) | ||
{ | ||
//console.log("change"); | ||
//console.log("windows", that.#windows); | ||
//console.log("newWindows", newWindows); | ||
if (that.#winChangeCallback) that.#winChangeCallback(); | ||
} | ||
} | ||
|
||
//console.log(that.#windows); | ||
}); | ||
|
||
window.addEventListener('beforeunload', function (e) | ||
{ | ||
let index = that.getWindowIndexFromId(that.#id); | ||
|
||
that.#windows.splice(index, 1); | ||
that.updateWindowsLocalStorage(); | ||
}); | ||
} | ||
|
||
#didWindowsChange (pWins, nWins) | ||
{ | ||
if (pWins.length != nWins.length) | ||
{ | ||
return true; | ||
} | ||
else | ||
{ | ||
let c = false; | ||
|
||
for (let i = 0; i < pWins.length; i++) | ||
{ | ||
if (pWins[i].id != nWins[i].id) c = true; | ||
} | ||
|
||
return c; | ||
} | ||
} | ||
|
||
|
||
init (metaData) | ||
{ | ||
this.#windows = JSON.parse(localStorage.getItem("windows")) || []; | ||
this.#count= localStorage.getItem("count") || 0; | ||
this.#count++; | ||
|
||
this.#id = this.#count; | ||
let shape = this.getWinShape(); | ||
this.#winData = {id: this.#id, shape: shape, metaData: metaData}; | ||
this.#windows.push(this.#winData); | ||
|
||
localStorage.setItem("count", this.#count); | ||
this.updateWindowsLocalStorage(); | ||
} | ||
|
||
getWinShape () | ||
{ | ||
let shape = {x: window.screenLeft, y: window.screenTop, w: window.innerWidth, h: window.innerHeight}; | ||
return shape; | ||
} | ||
|
||
getWindowIndexFromId (id) | ||
{ | ||
let index = -1; | ||
|
||
for (let i = 0; i < this.#windows.length; i++) | ||
{ | ||
if (this.#windows[i].id == id) index = i; | ||
} | ||
|
||
return index; | ||
} | ||
|
||
updateWindowsLocalStorage () | ||
{ | ||
localStorage.setItem("windows", JSON.stringify(this.#windows)); | ||
} | ||
|
||
update () | ||
{ | ||
//console.log(step); | ||
let winShape = this.getWinShape(); | ||
|
||
//console.log(winShape.x, winShape.y); | ||
|
||
if (winShape.x != this.#winData.shape.x || | ||
winShape.y != this.#winData.shape.y || | ||
winShape.w != this.#winData.shape.w || | ||
winShape.h != this.#winData.shape.h) | ||
{ | ||
|
||
this.#winData.shape = winShape; | ||
|
||
let index = this.getWindowIndexFromId(this.#id); | ||
this.#windows[index].shape = winShape; | ||
|
||
//console.log(windows); | ||
if (this.#winShapeChangeCallback) this.#winShapeChangeCallback(); | ||
this.updateWindowsLocalStorage(); | ||
} | ||
} | ||
|
||
setWinShapeChangeCallback (callback) | ||
{ | ||
this.#winShapeChangeCallback = callback; | ||
} | ||
|
||
setWinChangeCallback (callback) | ||
{ | ||
this.#winChangeCallback = callback; | ||
} | ||
|
||
getWindows () | ||
{ | ||
return this.#windows; | ||
} | ||
|
||
getThisWindowData () | ||
{ | ||
return this.#winData; | ||
} | ||
|
||
getThisWindowID () | ||
{ | ||
return this.#id; | ||
} | ||
} | ||
|
||
export default WindowManager; |
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,19 @@ | ||
<html> | ||
<head> | ||
<title>3d example using three.js and multiple windows</title> | ||
<script type="text/javascript" src="three.r124.min.js"></script> | ||
<style type="text/css"> | ||
|
||
* | ||
{ | ||
margin: 0; | ||
padding: 0; | ||
} | ||
|
||
</style> | ||
</head> | ||
<body> | ||
|
||
<script type="module" src="main.js"></script> | ||
</body> | ||
</html> |
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,183 @@ | ||
import WindowManager from './WindowManager.js' | ||
|
||
const t = THREE; | ||
let camera, scene, renderer, world; | ||
let near, far; | ||
let pixR = window.devicePixelRatio ? window.devicePixelRatio : 1; | ||
let cubes = []; | ||
let sceneOffsetTarget = {x: 0, y: 0}; | ||
let sceneOffset = {x: 0, y: 0}; | ||
|
||
let today = new Date(); | ||
today.setHours(0); | ||
today.setMinutes(0); | ||
today.setSeconds(0); | ||
today.setMilliseconds(0); | ||
today = today.getTime(); | ||
|
||
let internalTime = getTime(); | ||
let windowManager; | ||
let initialized = false; | ||
|
||
|
||
// this code is essential to circumvent that some browser preload the content of some pages before you actually hit the url | ||
document.addEventListener("visibilitychange", () => | ||
{ | ||
if (document.visibilityState != 'hidden' && !initialized) | ||
{ | ||
init(); | ||
} | ||
}); | ||
|
||
window.onload = () => { | ||
if (document.visibilityState != 'hidden') | ||
{ | ||
init(); | ||
} | ||
}; | ||
|
||
function init () | ||
{ | ||
// add a short timeout because window.offsetX reports wrong values before a short period | ||
setTimeout(() => { | ||
setupScene(); | ||
setupWindowManager(); | ||
resize(); | ||
updateWindowShape(false); | ||
render(); | ||
window.addEventListener('resize', resize); | ||
}, 500) | ||
} | ||
|
||
// get time in seconds since beginning of the day (so that all windows use the same time) | ||
function getTime () | ||
{ | ||
return (new Date().getTime() - today) / 1000.0; | ||
} | ||
|
||
|
||
function setupWindowManager () | ||
{ | ||
windowManager = new WindowManager(); | ||
windowManager.setWinShapeChangeCallback(updateWindowShape); | ||
windowManager.setWinChangeCallback(windowsUpdated); | ||
|
||
// here you can add your custom metadata to each windows instance | ||
let metaData = {foo: "bar"}; | ||
|
||
// this will init the windowmanager and add this window to the centralised pool of windows | ||
windowManager.init(metaData); | ||
|
||
// call update windows initially (it will later be called by the win change callback) | ||
windowsUpdated(); | ||
} | ||
|
||
function windowsUpdated () | ||
{ | ||
let wins = windowManager.getWindows(); | ||
|
||
updateNumberOfCubes(); | ||
} | ||
|
||
function updateNumberOfCubes () | ||
{ | ||
let wins = windowManager.getWindows(); | ||
|
||
// remove all cubes | ||
cubes.forEach((c) => { | ||
world.remove(c); | ||
}) | ||
|
||
cubes = []; | ||
|
||
// add new cubes | ||
for (let i = 0; i < wins.length; i++) | ||
{ | ||
let win = wins[i]; | ||
|
||
let c = new t.Color(); | ||
c.setHSL(i * .1, 1.0, .5); | ||
|
||
let s = 100 + i * 50; | ||
let cube = new t.Mesh(new t.BoxGeometry(s, s, s), new t.MeshBasicMaterial({color: c , wireframe: true})); | ||
cube.position.x = win.shape.x + (win.shape.w * .5); | ||
cube.position.y = win.shape.y + (win.shape.h * .5); | ||
|
||
world.add(cube); | ||
cubes.push(cube); | ||
} | ||
} | ||
|
||
function updateWindowShape (easing = true) | ||
{ | ||
sceneOffsetTarget = {x: -window.screenX, y: -window.screenY}; | ||
if (!easing) sceneOffset = sceneOffsetTarget; | ||
} | ||
|
||
function setupScene () | ||
{ | ||
camera = new t.OrthographicCamera(0, 0, window.innerWidth, window.innerHeight, -10000, 10000); | ||
|
||
camera.position.z = 2.5; | ||
near = camera.position.z - .5; | ||
far = camera.position.z + 0.5; | ||
|
||
scene = new t.Scene(); | ||
scene.background = new t.Color(0.0); | ||
scene.add( camera ); | ||
|
||
renderer = new t.WebGLRenderer({antialias: true, depthBuffer: true}); | ||
renderer.setPixelRatio(pixR); | ||
|
||
world = new t.Object3D(); | ||
scene.add(world); | ||
|
||
renderer.domElement.setAttribute("id", "scene"); | ||
document.body.appendChild( renderer.domElement ); | ||
} | ||
|
||
|
||
function render () | ||
{ | ||
let t = getTime(); | ||
|
||
windowManager.update(); | ||
|
||
let falloff = .05; | ||
sceneOffset.x = sceneOffset.x + ((sceneOffsetTarget.x - sceneOffset.x) * falloff); | ||
sceneOffset.y = sceneOffset.y + ((sceneOffsetTarget.y - sceneOffset.y) * falloff); | ||
|
||
world.position.x = sceneOffset.x; | ||
world.position.y = sceneOffset.y; | ||
|
||
let wins = windowManager.getWindows(); | ||
|
||
|
||
for (let i = 0; i < cubes.length; i++) | ||
{ | ||
let cube = cubes[i]; | ||
let win = wins[i]; | ||
let _t = t;// + i * .2; | ||
|
||
let posTarget = {x: win.shape.x + (win.shape.w * .5), y: win.shape.y + (win.shape.h * .5)} | ||
|
||
cube.position.x = cube.position.x + (posTarget.x - cube.position.x) * falloff; | ||
cube.position.y = cube.position.y + (posTarget.y - cube.position.y) * falloff; | ||
cube.rotation.x = _t * .5; | ||
cube.rotation.y = _t * .3; | ||
}; | ||
|
||
renderer.render(scene, camera); | ||
requestAnimationFrame(render); | ||
} | ||
|
||
|
||
function resize () | ||
{ | ||
let width = window.innerWidth; | ||
let height = window.innerHeight | ||
|
||
camera = new t.OrthographicCamera(0, width, 0, height, -10000, 10000); | ||
camera.updateProjectionMatrix(); | ||
renderer.setSize( width, height ); | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.