Three.js animation from blender - javascript

CURRENTLY, what's the best way to export skinned mesh animations from blender? I'm pretty sure I have the importing down, both for JSON and dae, but I can't get anything to work perfectly.
I've gotten JSON to the point where it seems like only a small portion of my animation will play, and that's after rolling back blender.
I thought it might have been the parent issue, so I tried no parent, parented, and as a modifier. I've tried all these variations with and without both the object and the armature selected and each seperately, I've tried to make sure there was only a single animation action, which from my understanding is fixed.
With dae, it seems to have some crazy scaling difference? And things seem to work sporadic. A model that will load into the editor (albeit w/no animations) in the examples won't load at all in the collada skinned mesh example/my created scene. And if I try to load a foreign model like the version of the avatar model from the three.js server, it shows up with no skin. And all of this required me to download the dev branch of three.js (at least to get my model to import at all)
My blend file:
http://97.95.26.94/examples/basic_animation.blend
I've spent three days working on this. It seems to be different for every version of blender/three.js
EDIT:
heres some code that I recently tried to get working. not sure where I was at on it, at this point I'm just trying every thing. Please ignore the loader above, thats for the rest of my scene. assume all the directories are right... because they are.
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>Lost Towers - Level designer</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<div id="monitor"
style=
"
position:absolute;
color:white;
"
>ladidda!!</div>
<script src='xxx/jquery.js'></script>
<script src='xxx/three.min.js'></script><script src="xxx/ColladaLoader.js"></script>
<script>
window.onclick= function() {
var temp= renderer.domElement ;
if(temp.requestPointerLock) temp.requestPointerLock();
else if(temp.mozRequestPointerLock) temp.mozRequestPointerLock();
else if(temp.webkitRequestPointerLock) temp.webkitRequestPointerLock();
}
/// initialize three.js
//init_scene();
var container;
container = document.createElement( 'div' );
document.body.appendChild( container );
//main renderer
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
renderer.shadowCameraNear = 3;
renderer.shadowCameraFar = 100;
renderer.shadowCameraFov = 10;
renderer.shadowMapBias = 0.03;
renderer.shadowMapWidth = 1024;
renderer.shadowMapHeight = 1024;
renderer.autoClear=false;
document.oncontextmenu=function(event){event.preventDefault();};
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
var manager = new THREE.LoadingManager();
var clock = new THREE.Clock();
var delta=0;
var loader = new THREE.BufferGeometryLoader();
var camera;
var scene= new THREE.Scene();
// load a resource
var loader = new THREE.ObjectLoader();
loader.setTexturePath("xxx");
loader.load= function (a, b, c, d) {////////////////Ignore all this, its for loading scene from clara.io
var e = loader,
f = new THREE.XHRLoader(e.manager);
f.setCrossOrigin(loader.crossOrigin);
f.load(a, function (a) {
var x=JSON.parse(a)
if(x.images)
for(var i=0;i<x.images.length;i++)
{
x.images[i].url=loader.texturePath+x.images[i].url;
}
e.parse(x, b)
}, c, d)
}
loader.load('xxx/main.json',function ( obj ) {
scene= obj;
//console.dir(obj);
loader.setTexturePath("xxx");
scene.traverse(function(child){
if(child instanceof THREE.PerspectiveCamera)
{
camera=child;
scene.add(camera);
}
/////////////////////////////////////HERE//////////////////////////////////////////////
});
var daeloader = new THREE.ColladaLoader();
daeloader.load( "xxx/main.dae", function ( collada ) {
console.dir(collada.scene); alert();
collada.scene.traverse( function ( obj ) {
if(obj.type=='Object3D')
obj.traverse( function ( child ) {
if ( child instanceof THREE.SkinnedMesh ) {
console.dir(child);
var animation = new THREE.Animation( child, child.geometry.animation );
animation.play();
camera.lookAt( child.position );
}
} );
} );
scene.add( collada.scene );
} );
},function(){loader.setTexturePath("xxx");});
main();
function main()
{
requestAnimationFrame( main );
THREE.AnimationHandler.update( clock.getDelta() );
if(scene && camera)
renderer.render( scene, camera );
}
</script>
</body>
</html>

Related

A-Frame async timing issue with component

I have written an A-Frame component that creates a canvas on which it writes an image. The image is generated dynamically from HTML. The HTML is created when a message arrives from a server, using WebSockets, asynchronously. The problem is that most often (but not always) the canvas is drawn blank. I have tested the component using hardcoded HTML, and it works just as expected, so the issue must be one of timing, but I don't know why or how to solve it.
Schematically, the code is:
In a JS module file, vr.js:
// Observe the arrival of the websocket message, then
// Create the HTML based on the message and save it as 'legend'
let legendBox = document.getElementById('legend')
legendBox.innerHTML = legend
legendBox.components.showHTML.update();
In the HTML file:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../js/vr.js" type="module"></script>
AFRAME.registerComponent('showHTML', {
init: function () {},
update: function () {
if (this.el.getObject3D('screen')) this.remove()
if (this.el.innerHTML.trim() === '') return
this.htmlcanvas = new HTMLCanvas(this.el)
let texture = new THREE.CanvasTexture(this.htmlcanvas.canvas)
texture.minFilter = THREE.LinearFilter
texture.wrapS = THREE.ClampToEdgeWrapping
texture.wrapT = THREE.ClampToEdgeWrapping
let material = new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
})
let geometry = new THREE.PlaneGeometry(1, 1)
let screen = new THREE.Mesh(geometry, material)
this.el.setObject3D('screen', screen)
this.screen = screen
this.resize()
},
resize() {
this.width = this.htmlcanvas.width / this.data.ppu
this.height = this.htmlcanvas.height / this.data.ppu
this.screen.scale.x = Math.max(0.0001, this.width)
this.screen.scale.y = Math.max(0.0001, this.height)
this.htmlcanvas.render()
},
remove: function () {
this.el.removeObject3D('screen')
},
})
class HTMLCanvas {
constructor(html) {
if (!html) throw 'Container Element is Required'
// Create the canvas to be drawn to
this.canvas = document.createElement('canvas')
this.ctx = this.canvas.getContext('2d')
// code to extract the innerHTML from the entity, transform it into an SVG,
// and load the SVG into an image -- all omitted for simplicity
// Renders the image containing the SVG to the Canvas
render() {
this.canvas.width = this.width
this.canvas.height = this.height
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.ctx.drawImage(this.img, 0, 0)
}
} // end class
} // end of component
</head>
<body>
<a-scene>
// usual A-Frame entities omitted
<a-entity id="legend" position="-1 1 -2" showHTML></a-entity>
// more A-Frame stuff including camera etc.
</a-scene>
</body>
</html>

THREE is not defined; how do I fix this?

I am currently experimenting with Three.js but I can't seem to fix this error, I've tried and searched for a lot of answers but I cant fix it.
I've included the threejs library
and in my script everything is written just fine
This is my html index file
<body>
<script src="script.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/242637/TrackballProjectorDetector.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/242637/AsciiEffect.js"></script>
</body>
This is some part of the script file
function init() {
var width = window.innerWidth || 2;
var height = window.innerHeight || 2;
container = document.createElement('div');
document.body.appendChild(container);
var info = document.createElement('div');
info.style.position = 'absolute';
info.style.top = '20px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'Drag to change the view';
container.appendChild(info);
camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000);
camera.position.y = 150;
camera.position.z = 500;
controls = new THREE.TrackballControls(camera);
scene = new THREE.Scene();
var light = new THREE.PointLight(0xffffff);
light.position.set(500, 500, 500);
scene.add(light);
var light = new THREE.PointLight(0xffffff, 0.25);
light.position.set(-500, -500, -500);
scene.add(light);
sphere = new THREE.Mesh(new THREE.SphereGeometry(200, 20, 10), new THREE.MeshLambertMaterial());
scene.add(sphere);
I get an error on the line:
camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000);
But when I remove it, the error goes down to the next THREE element.
<script src="script.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
I'd say the order may be important here. If the init function is run when loading script.js(a), THREE will not yet have been defined, since that would happen on the following line.
The solution, in that case, would be to load three.js before your script file.
(a) This is often done at the end of a js file but we can't be sure in this case, since we don't have the entire file. Hence I'd be looking at that file to see if it's something like:
function init() {
blahBlahBlah();
}
init();

How to center the three js canvas?

I have this really weird (possibly intended?) bug with my three js canvas.
I want to have it not fill the complete page and center the canvas inside a div.
As you can see in the code below, the canvas is inside the div. I even added some test headings to make sure I'm not going crazy here, but the canvas seems to move itself outside the div and I have no idea how to fix this.
<head>
<meta charset=utf-8>
<title>My first three.js app</title>
<style>
body { margin: 0; }
#test {
width: 100px;
height:100px;
margin: 0px auto;
border: 1px solid red;
}
</style>
<style type="text/css" src="css/main.css"></style>
</head>
<body>
<div id="test">
<h1>test1</h1>
<canvas id="canvasID"></canvas>
<h2>test2</h2>
</div>
<script src="https://github.com/mrdoob/three.js/blob/master/examples/js/Detector.js"></script>
<script src="three.js-master/build/three.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var canvas = document.getElementById("canvasID");
var renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize( window.innerWidth*0.9, window.innerHeight*0.9 );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
function render() {
requestAnimationFrame( render );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
}
render();
if (Detector.webgl) {
init();
animate();
} else {
var warning = Detector.getWebGLErrorMessage();
document.getElementById('container').appendChild(warning);
}
</script>
</body>
As you can see, the canvas has "magically" moved outside of its parent div
In your code you have this line document.body.appendChild( renderer.domElement ); which appends the canvas to the end of the <body> tag. So you are adding the canvas outside of your required div.
You can add your canvas inside that div with a simple javascript selector like:
document.getElementById('test').appendChild( renderer.domElement );
or
document.querySelector('#test').appendChild( renderer.domElement );
That way you are adding the renderer inside the div with the id="test". Let me know if it worked.

Three js keyboard rotation

can anyone help me in making a 3D object rotate on a y axis? with a press of keyboard button? (B)
I've been sitting on this issue for many days now, my code breaks every time i try to do something,
please please please
this is the 3D object that needs turning
https://www.dropbox.com/s/8yefhx3yc11zbik/b.obj?dl=0
and this is the code
var lesson6 = {
scene: null,
camera: null,
renderer: null,
container: null,
controls: null,
clock: null,
stats: null,
init: function() { // Initialization
this.scene = new THREE.Scene();
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
// prepare camera
var VIEW_ANGLE = 15, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 1, FAR = 0;
this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
this.scene.add(this.camera);
this.camera.position.set(10, 200, 0);
this.camera.lookAt(new THREE.Vector3(0,30,0));
// prepare renderer
this.renderer = new THREE.WebGLRenderer({ antialias:true });
this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
this.renderer.shadowMapEnabled = true;
this.renderer.shadowMapSoft = true;
// prepare container
this.container = document.createElement('div');
document.body.appendChild(this.container);
this.container.appendChild(this.renderer.domElement);
// events
THREEx.WindowResize(this.renderer, this.camera);
// prepare controls (OrbitControls)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target = new THREE.Vector3(0, 0, 0);
this.controls.maxDistance = 2000;
//keyboard control
// L I G H T S
//bottom
var light = new THREE.DirectionalLight(0xffffff, 1);
light.castShadow = true;
light.shadowCameraVisible = true;
light.shadowCameraNear = 100;
light.shadowCameraFar = 200;
light.shadowCameraLeft = -20; // CHANGED
light.shadowCameraRight = 20; // CHANGED
light.shadowCameraTop = 20; // CHANGED
light.shadowCameraBottom = -20; // CHANGED
light.position.set(180 ,20, 0); // CHANGED
this.scene.add(light);
this.scene.add( new THREE.DirectionalLightHelper(light, 0.2) );
//left
var dlight = new THREE.DirectionalLight(0xffffff, 1);
dlight.castShadow = true;
dlight.shadowCameraVisible = true;
dlight.shadowCameraNear = 100;
dlight.shadowCameraFar = 200;
dlight.shadowCameraLeft = -20; // CHANGED
dlight.shadowCameraRight = 20; // CHANGED
dlight.shadowCameraTop = 20; // CHANGED
dlight.shadowCameraBottom = -20; // CHANGED
dlight.position.set(0, 20, 100); // CHANGED
this.scene.add(dlight);
this.scene.add( new THREE.DirectionalLightHelper(dlight, 0.2) );
// add simple ground
// load a model
this.loadModel();
},
loadModel: function() {
// prepare loader and load the model
var oLoader = new THREE.OBJLoader();
oLoader.load('b.obj', function( geometry ) {
var material = new THREE.MeshLambertMaterial({ color: "red" });
var mesh = new THREE.Mesh (geometry, material);
geometry.traverse( function(child) {
if (child instanceof THREE.Mesh) {
// apply custom material
child.material = material;
child.castShadow = true;
child.receiveShadow = true;
}
});
geometry.rotation.y = -Math.PI / -1.7;
geometry.position.x = 0;
geometry.position.y = 0;
geometry.position.z = 0;
geometry.scale.set(1, 1, 1);
lesson6.scene.add(geometry);
});
}
};
// Animate the scene
function animate() {
requestAnimationFrame(animate);
render();
}
// Render the scene
function render() {
if (lesson6.renderer) {
lesson6.renderer.render(lesson6.scene, lesson6.camera);
}
}
// Initialize lesson on page load
function initializeLesson() {
lesson6.init();
animate();
}
if (window.addEventListener)
window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;
When dealing with that sort of input, you want the button press to NOT modify any object. You want your keyup/keydown/other input events to do as little as possible: toggle a true/false variable or change a number.
In this example:
function handleKeyDown(event) {
if (event.keyCode === 66) { //66 is "b"
window.isBDown = true;
}
}
function handleKeyUp(event) {
if (event.keyCode === 66) {
window.isBDown = false;
}
}
window.addEventListener('keydown', handleKeyDown, false);
window.addEventListener('keyup', handleKeyUp, false);
From there, you can modify your animate() function to be:
function animate() {
requestAnimationFrame(animate);
if (window.isBDown) {
//Increment your object's Y rotation value a little bit.
//Ideally, you would listen to animate()'s first argument,
//which is a high resolution timestamp that says the current time
//in milliseconds since the tab was opened.
}
render();
}
Note how all the work happens in animate(). Choose the correct speed, and the object will slowly rotate until you release the key. (Once comfortable with this concept, you can also add acceleration and momentum toanimate().)
Gamepad joysticks work the same way, except that instead of true or false, you are given a decimal number. You can multiply this into whatever value you use for "speed". Usually 0 means the joystick is centered, 1 means the joystick is maxed out at that axis, and some number between means input is being delivered, but not all the way (or is being pulled diagonally).

Texturing a sphere with Three.js does'nt work on smartphones

I have some troubles using Three.js. I want to apply a texture on a sphere (an image). My code works without any problem... until I try it on a smartphone. I try to look for the bug with Firefox and its remote debugger, but I did not find the issue. Here is my code :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
<meta name="viewport" content="initial-scale=1.0" />
<script src="./three.min.js"></script>
<script src="./sphere.js"></script>
<style>
html, body, #container {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container" style="width: 300px; height: 200px;"></div>
<script>
init();
</script>
</body>
</html>
and :
var renderer, scene, camera, mesh;
function init() {
var container = document.getElementById('container');
var width = container.offsetWidth;
var height = container.offsetHeight;
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(90, width/height, 1, 1000);
camera.position.set(0, 0, 300);
scene.add(camera);
var geometry = new THREE.SphereGeometry(200, 16, 16);
var texture = new THREE.Texture();
var loader = new THREE.ImageLoader();
var f = function(img) {
texture.needsUpdate = true;
texture.image = img;
var material = new THREE.MeshBasicMaterial({map: texture, overdraw: true});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
render();
animate();
}
loader.load('sphere.jpg', f);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.y += 0.003;
render();
}
function render() {
renderer.render(scene, camera);
}
What do I do wrong?
If you want to see the code in action, you can go here. Note that the problem is here with both WebGLRenderer and CanvasRenderer.
As a repost: The problem is solved by using power of two texture sizes.

Categories

Resources