Is there a way to play 360 video with Three js coming from m3u8 file?
I found a similar question here but no answer: https://github.com/mrdoob/three.js/issues/8216
https://threejs.org/examples/webgl_video_panorama_equirectangular.html
I have used the code from thee website for playing 360 video and this work fine when normal mp4 video url is used but when I try to include m3u8 then it fails with error.MEDIA_ERR_SRC_NOT_SUPPORTED:
The video could not be loaded, either because the server or network failed or because the format is not supported.
Here is the code:
<video id="video" width=960 height=540 style="display:none">
<source src="https://bitmovin.com/player-content/playhouse-vr/m3u8s/105560.m3u8" type="application/x-mpegURL">
</video>
var camera, scene, renderer;
var isUserInteracting = false,
lon = 0, lat = 0,
phi = 0, theta = 0,
distance = 50,
onPointerDownPointerX = 0,
onPointerDownPointerY = 0,
onPointerDownLon = 0,
onPointerDownLat = 0;
init();
animate();
function init() {
var container, mesh;
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
var geometry = new THREE.SphereBufferGeometry( 500, 60, 40 );
// invert the geometry on the x-axis so that all of the faces point inward
geometry.scale( - 1, 1, 1 );
var video = document.getElementById( 'video' );
video.play();
var texture = new THREE.VideoTexture( video );
var material = new THREE.MeshBasicMaterial( { map: texture } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.addEventListener( 'wheel', onDocumentMouseWheel, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting === true ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( onPointerDownPointerY - event.clientY ) * 0.1 + onPointerDownLat;
}
}
function onDocumentMouseUp() {
isUserInteracting = false;
}
function onDocumentMouseWheel( event ) {
distance += event.deltaY * 0.05;
distance = THREE.MathUtils.clamp( distance, 1, 50 );
}
function animate() {
requestAnimationFrame( animate );
update();
}
function update() {
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.MathUtils.degToRad( 90 - lat );
theta = THREE.MathUtils.degToRad( lon );
camera.position.x = distance * Math.sin( phi ) * Math.cos( theta );
camera.position.y = distance * Math.cos( phi );
camera.position.z = distance * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
renderer.render( scene, camera );
}
m3u8 plays on chrome (and some other browsers). I have been using it for years. The problem is in combining m3u8 format with three js
m3u8 works as can be seen here: https://hls-js.netlify.app/demo/ enter this url : https://bitmovin.com/player-content/playhouse-vr/m3u8s/105560.m3u8
HLS (.m3u8 file type) and MPEG DASH (.mpd file type) are adaptive bit rate streaming protocols (ABR).
ABR creates multiple bit rate versions of your content and chunks them, so the client can choose the next chunk from the best bit rate for the device and current network conditions (https://stackoverflow.com/a/42365034/334402).
To play a HSL or DASH file you typically use a Javascript based video player, which in turn will leverage the HTML5 Media Soure Extension API - HTML5 MSE:
https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API
The Javascript video player downloads the chunks of video, choosing the most appropriate bit rate for each chunk, and assembles them and passes them to the HTML5 video player.
Many Javascript Video Players will support 360 video and you may find it is easier to simply integrate one of these into your project. As an example, extensions to the commonly used videoJS player will support 360 video:
https://github.com/videojs/videojs-vr
Related
I’ve created a panoramic image with PerspectiveCamera and all went well. I’ve also managed to add controls.
Now I want to place a clickable element (link/button/whatever) in the scene but I have no clue how to add and position the clickable element…every tip/suggestion is highly appreciated! Here is my code sofar:
function init() {
const container = document.getElementById( 'three-image' );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
scene = new THREE.Scene();
const geometry = new THREE.SphereGeometry( 500, 60, 40 );
// invert the geometry on the x-axis so that all of the faces point inward
geometry.scale( - 1, 1, 1 );
//let imageLocation = <?php echo $block->getPanoramaImages();?>;
let imageLocation = '/three/luifel.jpg';
//alert(imageLocation)
const texture = new THREE.TextureLoader().load(imageLocation);
const material = new THREE.MeshBasicMaterial( { map: texture } );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
container.style.touchAction = 'none';
container.addEventListener( 'pointerdown', onPointerDown );
document.addEventListener( 'wheel', onDocumentMouseWheel );
window.addEventListener( 'resize', onWindowResize );
}
If you're looking to attach an html element to a 3d element, you can project a 3d position to 2d and use that.
var vector = new THREE.Vector3(100,0,0); // some point
vector.project(camera);
var widthHalf = window.innerWidth / 2;
var heightHalf = window.innerHeight / 2;
vector.x = vector.x * widthHalf + widthHalf;
vector.y = -(vector.y * heightHalf) + heightHalf;
vector.z = 0;
If you want to attach to an object, use the objects position in world space:
vector.setFromMatrixPosition(obj.matrixWorld);
vector.project(camera);
...etc
EDIT:
I've created an example here:
https://codepen.io/flatworldstudio/pen/LYbgvgY
I have implemented a three.js viewer for my panoramic pictures a couple of years ago and now that I'm moving my site to another location, I have an issue with Three.js not willing to load anymore.
Error message in the console:
DOMException: "The operation is insecure."
Here is the html part I'm using:
<div class="article-pano-gallery" style="display:table">
<img onclick="init('https://images.laurentwillen.be/sites/21/2017/05/photo-panoramique-360-Catane.jpg')" class="article-gallery-image pano-image" data-srcset="https://images.laurentwillen.be/sites/21/2017/05/photo-panoramique-360-Catane.jpg" src="https://images.laurentwillen.be/sites/21/2017/05/photo-panoramique-360-Catane-150x150.jpg" width="150" height="150">
</div>
And for the JS part:
<script async src="https://ajax.googleapis.com/ajax/libs/threejs/r84/three.min.js"></script>
<script>
var camera, scene, renderer;
var isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0;
function init(full_image)
{
var container, mesh;
container = document.getElementById( 'background-section' );
document.getElementById( 'background-section') .style.display='block';
document.getElementById( 'pano-loading-section') .style.display='block';
document.getElementById( 'close') .style.display='block';
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
var geometry = new THREE.SphereBufferGeometry( 500, 60, 40 );
// invert the geometry on the x-axis so that all of the faces point inward
geometry.scale( - 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( {
map: new THREE.TextureLoader().load( full_image )
} );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth*0.95, window.innerHeight*0.95 );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onPointerStart, false );
document.addEventListener( 'mousemove', onPointerMove, false );
document.addEventListener( 'mouseup', onPointerUp, false );
document.addEventListener( 'wheel', onDocumentMouseWheel, false );
document.addEventListener( 'touchstart', onPointerStart, false );
document.addEventListener( 'touchmove', onPointerMove, false );
document.addEventListener( 'touchend', onPointerUp, false );
document.addEventListener( 'dragover', function ( event )
{
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
}, false );
document.addEventListener( 'dragenter', function ()
{
document.body.style.opacity = 0.5;
}, false );
document.addEventListener( 'dragleave', function ()
{
document.body.style.opacity = 1;
}, false );
document.addEventListener( 'drop', function ( event )
{
event.preventDefault();
var reader = new FileReader();
reader.addEventListener( 'load', function ( event ) {
material.map.image.src = event.target.result;
material.map.needsUpdate = true;
}, false );
reader.readAsDataURL( event.dataTransfer.files[ 0 ] );
document.body.style.opacity = 1;
}, false );
//
window.addEventListener( 'resize', onWindowResize, false );
document.getElementById( 'pano-loading-section') .style.display='none';
animate();
}
function onWindowResize()
{
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth*0.95, window.innerHeight*0.95 );
}
function onPointerStart( event )
{
isUserInteracting = true;
var clientX = event.clientX || event.touches[ 0 ].clientX;
var clientY = event.clientY || event.touches[ 0 ].clientY;
onMouseDownMouseX = clientX;
onMouseDownMouseY = clientY;
onMouseDownLon = lon;
onMouseDownLat = lat;
}
function onPointerMove( event )
{
if ( isUserInteracting === true )
{
var clientX = event.clientX || event.touches[ 0 ].clientX;
var clientY = event.clientY || event.touches[ 0 ].clientY;
lon = ( onMouseDownMouseX - clientX ) * 0.1 + onMouseDownLon;
lat = ( clientY - onMouseDownMouseY ) * 0.1 + onMouseDownLat;
}
}
function onPointerUp()
{
isUserInteracting = false;
}
function onDocumentMouseWheel( event )
{
var fov = camera.fov + event.deltaY * 0.05;
camera.fov = THREE.Math.clamp( fov, 10, 75 );
camera.updateProjectionMatrix();
}
function animate()
{
requestAnimationFrame( animate );
update();
}
function update()
{
if ( isUserInteracting === false )
{
lon += 0.1;
}
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
/*
// distortion
camera.position.copy( camera.target ).negate();
*/
renderer.render( scene, camera );
}
</script>
I was using the same script on another domain for images and it was working well but now that I have moved, it doesn't work anymore.
I suppose it has something to do with cross domain but I don't see where exactly. I have tried a local version of the JS file and it's the same issue.
The error in the JS occurs on line 121. Here is the full text as requested below:
DOMException: "The operation is insecure." three.js:121:396
texImage2D https://wp.laurentwillen.be/js/three.js:121
n https://wp.laurentwillen.be/js/three.js:97
setTexture2D https://wp.laurentwillen.be/js/three.js:181
hf https://wp.laurentwillen.be/js/three.js:7
upload https://wp.laurentwillen.be/js/three.js:343
G https://wp.laurentwillen.be/js/three.js:147
renderBufferDirect https://wp.laurentwillen.be/js/three.js:165
n https://wp.laurentwillen.be/js/three.js:134
render https://wp.laurentwillen.be/js/three.js:180
update https://wp.laurentwillen.be/circuits/circuit-italie/catane/?bb:1679
animate https://wp.laurentwillen.be/circuits/circuit-italie/catane/?bb:1659
Thanks
This is definitely a CORS issue. I recommend you get yourself acquainted with how modern browsers handle cross-domain requests.
My best guess is that your images.laurentwillen.be server isn't configured to allow delivering assets to other domains. This is a safety feature so other people don't steal your bandwidth (you probably wouldn't want 1000s of sites freeloading by using images hosted on your server).
There are 2 solutions to this problem:
Host the images in the same domain where they're being requested.
Figure out how to let your server allow delivery of images to the domain where you're using them. The way to achieve this depends on the type of server you're running and you'll have to do some digging to figure out how to configure it, so solution 1 would be easiest.
I am using Three.js with the gltf loader to load a single .glb resource onto a page. It works locally, though then I upload to Netlify the model does not load and the xhr progressEvent's total is 0. I checked that the model is still being loaded in the network tab, but yet it still does not show in the page.
It seems this problem has occurred before, but not sure how to resolve it when using Netlify if there are any environment variables I need to change.
https://github.com/mrdoob/three.js/issues/15584
HTML
<div class="model-wrapper">
<div id="model_target" class="loading">
<div class="myimage fade" id="placeholder">
<img src="images/placeholder.png" height="328px"/></div>
</div>
</div>
<!-- THREE.js -->
<script src="https://threejs.org/build/three.js"></script>
<!-- GLTFLoader.js -->
<script src="https://cdn.rawgit.com/mrdoob/three.js/r92/examples/js/loaders/GLTFLoader.js"></script>
JS
```
let camera, scene, renderer;
const mouse = new THREE.Vector2();
const look = new THREE.Vector2();
const windowHalf = new THREE.Vector2( window.innerWidth /
2, window.innerHeight / 2 );
var plane = new THREE.Plane(new THREE.Vector3(0, 0, 0.4),
-9);
var raycaster = new THREE.Raycaster();
var pointOfIntersection = new THREE.Vector3();
let modelLoaded = false;
let placement = document.getElementById("model_target")
window.addEventListener('DOMContentLoaded', init);
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 60, 1, 1, 1000);
camera.position.set(5, 3, 28)
//camera.position.y = 13;
var light = new THREE.DirectionalLight("#fff", 1.5);
var ambient = new THREE.AmbientLight("#FFF");
light.position.set( 0, -70, 100 ).normalize();
scene.add(light);
// scene.add(ambient);
var texture = new THREE.Texture();
var loader = new THREE.GLTFLoader();
THREE.Cache.enabled = true;
// Load a glTF resource
loader.load(
// 3d model resource
'./assets/models/mrktechy3.glb',
// called when the resource is loaded
function ( gltf ) {
mesh = gltf.scene;
mesh.scale.set( 5, 5, 5 );
scene.add( mesh );
},
// called when loading is in progress
function ( xhr ) {
// Loading progress of model
console.log(xhr);
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
if((xhr.loaded / xhr.total * 100) == 100){
modelLoaded = true;
//Loading overlay
var placeholder = document.getElementById("placeholder");
placeholder.classList.add("faded");
placement.classList.remove("loading");
}
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened' );
}
);
//scene.background = new THREE.Color(0xfff); //Set background color
renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } );
renderer.setSize( 800, 500 );
placement.appendChild( renderer.domElement );
renderer.setClearColor(0x000000, 0);
renderer.gammaOutput = true;
document.addEventListener( 'mousemove', onMouseMove, false );
window.addEventListener( 'resize', onResize, false );
render()
}
function onMouseMove( event ) {
if (modelLoaded){
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 0;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 0;
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(plane, pointOfIntersection);
mesh.lookAt(pointOfIntersection);
}
}
function onResize( event ) {
const width = 800 ;
const height = 500;
windowHalf.set( width / 2, height / 2 );
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
}
var easeAmount = 8;
function update(){
look.x += (mouse.x-look.x)/easeAmount;
look.y += (mouse.y-look.y)/easeAmount;
raycaster.setFromCamera(look, camera);
raycaster.ray.intersectPlane(plane, pointOfIntersection);
mesh.lookAt(pointOfIntersection);
}
function render() {
camera.aspect = renderer.domElement.clientWidth / renderer.domElement.clientHeight;
camera.updateProjectionMatrix();
requestAnimationFrame( render );
if (modelLoaded){
update();
}
renderer.render( scene, camera );
}
```
Any glb resource can be replaced in the code and it works locally but not when hosted.
In the IIS settings, try to add mime type for gltf. Something like this:
application/gltf-buffer .gltf
This solved my problem.
I'm trying to find a way to animate the children in a Collada model of the Canadarm2. The model contains 7 arm segments whose angles need to be set by reading in rotation angle data from each of 7 spreadsheet columns one row at a time. Each line holds 7 columns of data representing each second's angles in an hour, or longer, mission.
I'm hoping someone might suggest a solution to get me started -- perhaps something like the guesswork I've tried inside the animate() function below.
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats, clock;
var camera, scene, renderer, canadarm2;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 2000 );
camera.position.set( 8, 10, 80 );
camera.lookAt( new THREE.Vector3( 0, 3, 0 ) );
scene = new THREE.Scene();
clock = new THREE.Clock();
// loading manager
var loadingManager = new THREE.LoadingManager( function() {
scene.add( canadarm2 );
} );
// collada
var loader = new THREE.ColladaLoader( loadingManager );
//loader.load( 'examples/models/collada/elf/elf.dae', function ( collada ) {
loader.load( 'canadarm2.dae', function ( collada ) {
canadarm2 = collada.scene;
} );
//
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 1, 1, 0 ).normalize();
scene.add( directionalLight );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//
stats = new Stats();
container.appendChild( stats.dom );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
/*
//#######################################
// doesn't work
var armEP = canadarm2.getChildByName("EP", true);// EP is one of 7 arms
armEP.position.x += 0.01;
armEP.rotation.y += 45;
//#######################################
*/
render();
stats.update();
}
function render() {
var delta = clock.getDelta();
if ( canadarm2 !== undefined ) {
canadarm2.rotation.z += delta * 0.5;
}
renderer.render( scene, camera );
}
This seems to work although it seems an awfully roundabout way to locate the arms.
scene.traverse(function (child) {
switch(child.name) {
case "SR":
child.rotation.x += 0.01;
break;
case "SP":
child.rotation.y += 0.01;
break;
case "SY":
child.rotation.y += 0.01;
break;
case "EP":
child.rotation.y += 0.01;
break;
case "WP":
child.rotation.x += 0.01;
break;
case "WY":
child.rotation.y += 0.01;
break;
case "WR":
child.rotation.x += 0.01;
break;
}
});
I am fairly new to three.js and have a problem I can't readily find an answer for.
Here is a codepen that should sum up the situation: http://codepen.io/anon/pen/PPYPzO
var container, stats;
var camera, controls, scene, renderer, raycaster, mouse;
init();
animate();
add_world();
var indie_render = true;
for(var j = 0; j < 20; j++){
add_objects(20);
indie_render = !indie_render;
console.log("adding more objects...");
if(!indie_render){render();}
}
function add_world(){
var geometry = new THREE.BoxGeometry( 1000, 1000, 1000);
var mesh = new THREE.MeshBasicMaterial( {color: 0xf5f5dc, wireframe: false, opacity: 0.2, transparent:true } );
var world = new THREE.Mesh( geometry, mesh );
scene.add( world );
render();
}
function add_objects(num, indiv){
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { shading: THREE.FlatShading } );
material.color.setRGB( Math.random(), Math.random(), Math.random() );
for ( var i = 0; i < num; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = ( Math.random() - 0.5 ) * 1000;
mesh.position.y = ( Math.random() - 0.5 ) * 1000;
mesh.position.z = ( Math.random() - 0.5 ) * 1000;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
if(indie_render){
console.log("individual render");
render();
}
}
}
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set(500, 500, -1000);
camera.up.set( 0, 1, 0 );
camera.lookAt(500,500,500);
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
//world
scene = new THREE.Scene();
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( 0x000000, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
container.addEventListener( 'mousemove', onMouseMove, false );
container.addEventListener( 'mousedown', onMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
function onMouseMove( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
}
function onMouseDown( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
if(e.button == 2){ //right button
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children, true );
if ( intersects.length > 0 ) {
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { color:0xff0000, shading: THREE.FlatShading } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
scene.add(mesh);
render();
}
}
}
In this demo, I init() and animate() a blank scene, and then add a translucent cube, following what seems to be convention. Then I add groups of spheres to the scene in a nested for loop, randomly placing the spheres inside the cube and making a render() call after every scene.add() call.
Currently, the scene adds all the spheres and only then is visible to the user, even though I can add individual objects after the for-loop objects are added (by right-clicking on the cube). I need for the user to be able to watch as spheres are added, rather than waiting for the whole thing to be done.
I realize this may not be the most efficient way to render the scene, but it would be quite helpful if, for example, the info on the objects to be animated is arriving asynchronously from a server. Does anyone have a suggestion?
Thanks
1) First: move call render() to animate:
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
2) Call add_objects asynchronously: setTimeout( add_objects, 0, 20 );
http://codepen.io/anon/pen/bVbEEP