I am trying out the following using three.js, where I have 2 shapes: “squares” and a “sphere”.
Each object has a button associated with it. If the user clicks a button, the corresponding shape will be added to the scene however, there can only be 1 square and 1 sphere in a scene at any one time.
If the user adds a new square, it should replace the old one. I seem to be having trouble achieving this though with my code below. Currently, I can successfully add the square and sphere objects however, when I click to add another square, the current square doesn’t get replaced.
Please advise, thank you!
<html>
<head>
<title>My second three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<button id="button1">Square1</button>
<button id="button2">Square2</button>
<button id="button3">Sphere</button>
<script src="js/three.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
loader.load( ‘square1.glb’, function ( geometry ) {
geometry.scene.traverse( function ( node ) {
if ( node.isMesh ){
square = node;
square.material.map.anisotropy = maxAnisotropy;
}
document.getElementById("button1").addEventListener("click", function(){
scene.remove(square);
scene.add(square);
});
});
loader.load( ‘square2.glb’, function ( geometry ) {
geometry.scene.traverse( function ( node ) {
if ( node.isMesh ){
square = node;
square.material.map.anisotropy = maxAnisotropy;
}
document.getElementById("button2").addEventListener("click", function(){
scene.remove(square);
scene.add(square);
});
});
loader.load( ‘sphere.glb’, function ( geometry ) {
geometry.scene.traverse( function ( node ) {
if ( node.isMesh ){
sphere = node;
sphere.material.map.anisotropy = maxAnisotropy;
}
document.getElementById("button3").addEventListener("click", function(){
scene.add(sphere);
});
});
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
Consider tracking the "current square" shape outside the scope of your <button> click handlers, to ensure that the logic for adding and removing these shape from the scene functions as expected.
Specifically, you'll want to call scene.remove() with the current square shape (if present), rather than to call it with the shape object that you subsequently call scene.add() with:
/* Current code */
document.getElementById("button1").addEventListener("click", function(){
scene.remove(square); /* You're removing the square object here, */
scene.add(square); /* and then adding the same square object to the scene */
});
You might find it useful to adapt your script as follows:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
/*
Not sure where you get your loader object from
*/
var loader = ''; // ??
/*
Add this to track current shapes
*/
var currentSquare = '';
var currentSphere = '';
/*
Define a general, reusable method for loading geometry
*/
function loadShape(filename, onLoaded) {
loader.load( filename, function ( geometry ) {
var foundMesh = '';
geometry.scene.traverse( function ( node ) {
if ( node.isMesh ){
foundMesh = node;
foundMesh.material.map.anisotropy = maxAnisotropy;
}
});
if(foundMesh) {
onLoaded(foundMesh)
}
}
}
/*
Use the general loader method defined above
*/
loadShape('square1.glb', function(nextSquare) {
document.getElementById("button1").addEventListener("click", function(){
/*
If current square exists, remove it
*/
if(currentSquare) {
scene.remove(currentSquare);
}
/*
Add the square for the button just clicked
and update currentSquare
*/
scene.add(nextSquare);
currentSquare = nextSquare;
});
})
loadShape('square2.glb', function(nextSquare) {
document.getElementById("button2").addEventListener("click", function(){
if(currentSquare) {
scene.remove(currentSquare);
}
scene.add(nextSquare);
currentSquare = nextSquare;
});
})
loadShape('sphere.glb', function(nextSphere) {
document.getElementById("button3").addEventListener("click", function(){
if(currentSphere) {
scene.remove(currentSphere);
}
scene.add(nextSphere);
currentSphere = nextSphere;
});
})
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
Hope this helps!
Related
I'm trying to load an animated model(.glb) in three.js but I get the above error.
If I paste the Load animated model function in the main method then it works but if I use it in a seperate class then it doesn't work anymore. Also the LoadStaticModel function does work but not the animated function. Any ideas what's wrong?
Thanks in advance!
Here is the code:
class CharacterControllerInput{
mixers = [];
scene;
constructor(scene){
this.scene = scene;
this.LoadAnimatedModel();
}
LoadAnimatedModel(scene){
this.scene = scene;
const loader = new GLTFLoader();
loader.load( 'http://127.0.0.1:3000/docs/BeterWerktDit.glb', function ( gltf ) {
gltf.scene.traverse( function ( object ) {
if ( object.isMesh ) object.castShadow = true;
} );
const model1 = SkeletonUtils.clone( gltf.scene );
const mixer1 = new THREE.AnimationMixer( model1 );
mixer1.clipAction( gltf.animations[ 0 ] ).play();
this.scene.add( model1);
this.mixers.push( mixer1);
render();
} );
}
Here is an abbreviated version of the class where I instantiate the Class.
class Scene{
constructor(){
this.main();
}
main(){
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});
const scene = new THREE.Scene();
this.character = new CharacterControllerInput(scene);
render();
function render(){
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false);
const delta = clock.getDelta();
for (const mixer of mixers) mixer.update(delta);
renderer.render(scene, camera);
requestAnimationFrame(render)
}
requestAnimationFrame(render);
}
};
let _APP = null;
window.addEventListener('DOMContentLoaded', () => {
_APP = new Scene();
});
I resolved the error. The problem was that I used this.scene in the callback function. So this. refers to the callback function.
The code below works:
class CharacterControllerInput {
scene;
constructor(scene, mixers) {
this.scene = scene;
this.mixers = mixers;
this.LoadAnimatedModel();
}
_init() {
this.LoadAnimatedModel();
}
LoadAnimatedModel() {
const loader = new GLTFLoader();
const scene = this.scene;
const mixers = this.mixers;
loader.load('http://127.0.0.1:3000/docs/BeterWerktDit.glb', function (gltf) {
gltf.scene.traverse(function (object) {
if (object.isMesh) object.castShadow = true;
});
const model = SkeletonUtils.clone(gltf.scene);
const mixer = new THREE.AnimationMixer(model);
mixer.clipAction(gltf.animations[0]).play();
scene.add(model);
mixers.push(mixer);
});
}
How do you do this? I created a scene using the Three.js Editor and downloaded the project using the "Publish" option. I edited the app.js file to import OrbitControls, so now I can use the mouse wheel (or in my case the Apple Magic Mouse 2 trackpad surface) to zoom in/out of the scene. However, I don't want the mouse wheel to zoom, I want it to "scroll" (move the camera up/down along the Y-axis as opposed the Z-axis). Is there a way to do that? Is OrbitControls the way to get there or does that just add complexity? Here is my app.js:
import {OrbitControls} from './OrbitControls.js'; // Added
var APP = {
Player: function ( THREE ) {
window.THREE = THREE; // FIX for editor scripts (they require THREE in global namespace)
var loader = new THREE.ObjectLoader();
var camera, scene, renderer;
var events = {};
var dom = document.createElement( 'div' );
dom.className = "threejs-app";
this.dom = dom;
this.width = 500;
this.height = 500;
this.load = function ( json ) {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.setClearColor( 0x000000 );
renderer.setPixelRatio( window.devicePixelRatio );
var project = json.project;
if ( project.shadows ) renderer.shadowMap.enabled = true;
if ( project.vr ) renderer.xr.enabled = true;
dom.appendChild( renderer.domElement );
this.setScene( loader.parse( json.scene ) );
this.setCamera( loader.parse( json.camera ) );
var controls = new OrbitControls( camera, renderer.domElement ); // Added
events = {
init: [],
start: [],
stop: [],
keydown: [],
keyup: [],
mousedown: [],
mouseup: [],
mousemove: [],
touchstart: [],
touchend: [],
touchmove: [],
update: []
};
var scriptWrapParams = 'player,renderer,scene,camera';
var scriptWrapResultObj = {};
for ( var eventKey in events ) {
scriptWrapParams += ',' + eventKey;
scriptWrapResultObj[ eventKey ] = eventKey;
}
var scriptWrapResult = JSON.stringify( scriptWrapResultObj ).replace( /\"/g, '' );
for ( var uuid in json.scripts ) {
var object = scene.getObjectByProperty( 'uuid', uuid, true );
if ( object === undefined ) {
console.warn( 'APP.Player: Script without object.', uuid );
continue;
}
var scripts = json.scripts[ uuid ];
for ( var i = 0; i < scripts.length; i ++ ) {
var script = scripts[ i ];
var functions = ( new Function( scriptWrapParams, script.source + '\nreturn ' + scriptWrapResult + ';' ).bind( object ) )( this, renderer, scene, camera );
for ( var name in functions ) {
if ( functions[ name ] === undefined ) continue;
if ( events[ name ] === undefined ) {
console.warn( 'APP.Player: Event type not supported (', name, ')' );
continue;
}
events[ name ].push( functions[ name ].bind( object ) );
}
}
}
dispatch( events.init, arguments );
};
this.setCamera = function ( value ) {
camera = value;
camera.aspect = this.width / this.height;
camera.updateProjectionMatrix();
};
this.setScene = function ( value ) {
scene = value;
};
this.setSize = function ( width, height ) {
this.width = width;
this.height = height;
if ( camera ) {
camera.aspect = this.width / this.height;
camera.updateProjectionMatrix();
}
if ( renderer ) {
renderer.setSize( width, height );
}
};
function dispatch( array, event ) {
for ( var i = 0, l = array.length; i < l; i ++ ) {
array[ i ]( event );
}
}
var time, prevTime;
function animate() {
time = performance.now();
try {
dispatch( events.update, { time: time, delta: time - prevTime } );
} catch ( e ) {
console.error( ( e.message || e ), ( e.stack || "" ) );
}
renderer.render( scene, camera );
prevTime = time;
}
this.play = function () {
prevTime = performance.now();
document.addEventListener( 'keydown', onDocumentKeyDown );
document.addEventListener( 'keyup', onDocumentKeyUp );
document.addEventListener( 'mousedown', onDocumentMouseDown );
document.addEventListener( 'mouseup', onDocumentMouseUp );
document.addEventListener( 'mousemove', onDocumentMouseMove );
document.addEventListener( 'touchstart', onDocumentTouchStart );
document.addEventListener( 'touchend', onDocumentTouchEnd );
document.addEventListener( 'touchmove', onDocumentTouchMove );
dispatch( events.start, arguments );
renderer.setAnimationLoop( animate );
};
this.stop = function () {
document.removeEventListener( 'keydown', onDocumentKeyDown );
document.removeEventListener( 'keyup', onDocumentKeyUp );
document.removeEventListener( 'mousedown', onDocumentMouseDown );
document.removeEventListener( 'mouseup', onDocumentMouseUp );
document.removeEventListener( 'mousemove', onDocumentMouseMove );
document.removeEventListener( 'touchstart', onDocumentTouchStart );
document.removeEventListener( 'touchend', onDocumentTouchEnd );
document.removeEventListener( 'touchmove', onDocumentTouchMove );
dispatch( events.stop, arguments );
renderer.setAnimationLoop( null );
};
this.dispose = function () {
while ( dom.children.length ) {
dom.removeChild( dom.firstChild );
}
renderer.dispose();
camera = undefined;
scene = undefined;
renderer = undefined;
};
//
function onDocumentKeyDown( event ) {
dispatch( events.keydown, event );
}
function onDocumentKeyUp( event ) {
dispatch( events.keyup, event );
}
function onDocumentMouseDown( event ) {
dispatch( events.mousedown, event );
}
function onDocumentMouseUp( event ) {
dispatch( events.mouseup, event );
}
function onDocumentMouseMove( event ) {
dispatch( events.mousemove, event );
}
function onDocumentTouchStart( event ) {
dispatch( events.touchstart, event );
}
function onDocumentTouchEnd( event ) {
dispatch( events.touchend, event );
}
function onDocumentTouchMove( event ) {
dispatch( events.touchmove, event );
}
}
};
export { APP };
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
I don't have the details of your project but seems like you need implement your own stroller module.
How can I do that, Teo?
Well, my young Padawan, we just need to implement a function that moves the camera up/down as you mentioned.
So, you mean that OrbitControls just add complexity?
Now, if that is the only camera movement you need, then yupe it does.
Let's implement it.
First, we add window.addEventListener('wheel', onMouseWheel, false); to listen the mouse wheel. Then we call event.preventDefault(); method to prevent the zoom.
Now, we calculate the scroll modifier camera.position.y += event.deltaY / 1000;. In this case, we use the delta in the Y-axis. For this example the value was so high so I divided by 1000 to have a more smooth scrolling.
Finally, we add camera.position.clampScalar(0, 10); to prevent the scrolling out of bounds.
let camera, scene, renderer;
let geometry, material, mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 1;
scene = new THREE.Scene();
geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('wheel', onMouseWheel, false);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
function onMouseWheel(ev) {
event.preventDefault();
camera.position.y += event.deltaY / 1000;
// prevent scrolling beyond a min/max value
camera.position.clampScalar(0, 10);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
If vertical scroll is the only behavior you need, then yes, adding OrbitControls is adding unnecessary complexity because you could easily achieve it with the default JavaScript APIs:
// Add listener to respond to the "wheel" event
window.addEventListener("wheel", onMouseWheel);
function onMouseWheel(event){
camera.position.y += event.deltaY * 10;
}
The * 10 multiplier depends on the size of your scene, and how fast you want your camera to move vertically.
I try to understand how to embed three.js scene with few toggles inside of a bootstrap grid, for some reason my canvas which is inside column refuse to be in same row with the text column and i cant figure out why, any help will be much appreciated.
Preview: https://htmlpreview.github.io/?https://github.com/Phoreaka/PlateInGrid/blob/master/index.html
Html:
<section id="intro">
<div class="container-fluid">
<div class="row">
<div class="col-lg-4 col-md-4 textContainer ">
<p>There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.</p>
</div> <!-- col end -->
<div class="col-lg-8 col-md-8">
<div id="myCanvas"></div>
<button id="polychrome"></button>
<button id="blue"></button>
<button id='PausePlay'>Pause</button>
</div> <!-- col end --->
</div> <!--row end -->
</div> <!-- container end -->
</section>
Css:
html, body {
direction: rtl;
font-family: 'Heebo', sans-serif;
-webkit-font-smoothing: antialiased;
margin: 0px;
overflow: hidden;
}
.background {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
z-index: -99;
background-color: lightgray;
background-repeat: no-repeat;
background-size: cover;
}
#intro {
width: 100wh;
height: 100%;
}
.textContainer {
height: auto;
font-size: 22px;
}
#myCanvas {
}
canvas {
height: 0;
}
#polychrome {
background-color: rgba(246, 244, 231, 1);
border-radius: 50%;
width: 20px;
height: 20px;
bottom: 20px;
border: 2px solid #194E9F;
}
#blue {
background-color: rgba(246, 244, 231, 1);
border-radius: 50%;
width: 20px;
height: 20px;
bottom: 20px;
border: 2px solid #194E9F;
}
#PausePlay {
background-color: transparent;
bottom: 20px;
border-style: none;
}
Javascript:
var container, stats, plate, plateTwo;
//loading objects into scene
var camera, scene, renderer, plate, plateTwo, controls; //loading objects into scene
canvas_width = 700,
canvas_height = 700;
//Pause button toggle-Boolean for start and restart
var initAnim = true;
var runAnim = false;
var isPlay = true;
//tweening
init();
animate();
//init functions for all the scene
function init() {
container = document.getElementById( 'myCanvas' ); //creates div and inside of it canvas
document.body.appendChild( container );
// scene
scene = new THREE.Scene();
//camera
camera = new THREE.PerspectiveCamera( 40,canvas_width / canvas_height, 100, 100000 );
camera.position.set( 0, 0, 1000 );
//lights
var ambient = new THREE.AmbientLight( 0xFFFFf3, 1.1 );
ambient.position.set( 0, 0, 900 ).normalize();
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.6 );
directionalLight.position.set( 0, 0, 700 ).normalize();
scene.add( directionalLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.6 );
directionalLight.position.set( 0, 0, -700 ).normalize();
scene.add( directionalLight );
//lights end
//setting renderer
renderer = new THREE.WebGLRenderer({
alpha: true});//background opacity
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( canvas_width, canvas_height );
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
//setting render end
//orbit controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target = new THREE.Vector3(-300,0,0);
controls.minPolarAngle = 1; // radians
controls.maxPolarAngle = Math.PI/2; // radians
controls.minDistance = 500;
controls.maxDistance = 800;
controls.enablePan = false;
//orbit controls end
// model blue
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round(percentComplete, 2) + '% downloaded' );
}
};
var onError = function ( xhr ) { };
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() ); //helper for texture loading
//texture loader
var mtlLoader = new THREE.MTLLoader();
mtlLoader.crossOrigin = ''; //cross origin-allows to run it on github
mtlLoader.setPath( 'images/new/' );
mtlLoader.load( 'BluePlate.mtl', function( materials ) {
materials.preload();
//model loader
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( 'images/new/' );
objLoader.load( 'BluePlate.obj', addPlate);
});
//object position setings
var addPlate = function(object){
plate = object;
plate.name = 'blue_plate';
//Move the plate in the scene
plate.rotateX(0);
plate.rotateY(0);
plate.rotateZ(30);
plate.position.x = -300;
plate.position.y = 0;
plate.position.z = 0;
plate.scale.y = 1.8;
plate.scale.x = 1.8;
plate.scale.z = 1.8;
//Add the 3D object in the scene
scene.add(plate);//adds the plate
animate(plate);
render();
};
//model blue end
//model Polychrome inside of button function appears only when "#polychrome is pressed"
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round(percentComplete, 2) + '% downloaded' );
}
};
var onError = function ( xhr ) { };
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() ); //helper for texture loading
//texture loader
var mtlLoaderTwo = new THREE.MTLLoader();
mtlLoaderTwo.crossOrigin = ''; //cross origin-allows to run it on github
mtlLoaderTwo.setPath( 'images/new/' );
mtlLoaderTwo.load( 'PolychromePlate.mtl', function( materials ) {
materials.preload();
//model loader
var objLoaderTwo = new THREE.OBJLoader();
objLoaderTwo.setMaterials( materials );
objLoaderTwo.setPath( 'images/new/' );
objLoaderTwo.load( 'PolychromePlate.obj', addPlateTwo);
});
//object position setings
var addPlateTwo = function(object){
plateTwo = object;
plateTwo.name = "color_plate";
//Move the plate in the scene
plateTwo.rotateX(0);
plateTwo.rotateY(0);
plateTwo.rotateZ(30);
plateTwo.position.x = -300;
plateTwo.position.y = 0;
plateTwo.position.z = 0;
plateTwo.scale.y = 1.8;
plateTwo.scale.x = 1.8;
plateTwo.scale.z = 1.8;
}
//polychrome model end
//Add the 3D object in the scene
var changeTexture = function(){
if(!(scene.getObjectByName('color_plate')))
{
scene.remove(plate);//removes the blue plate from scene
scene.add(plateTwo);
animate(plateTwo);
render();
}
};
//function for button blue to switch back to blue texture
function changeTextureBlue(){
if(!(scene.getObjectByName('blue_plate')))
{
scene.add(plate);
scene.remove(plateTwo);//removes the polychrome
}
}
//mouse click - running the functions of button click need to appear after the model load
document.getElementById('polychrome').addEventListener('click', changeTexture, false);
document.getElementById('blue').addEventListener('click', changeTextureBlue, false);
//mouse click end
//pause toggle
var startButton = document.getElementById( 'PausePlay' );
// Start Button
startButton.onclick = function StartAnimation() {
// Start and Pause
if (runAnim) {
startButton.innerHTML = 'Pause';
runAnim = false;
isPlay = true;
animate();
} else {
startButton.innerHTML = 'Play';
runAnim = true;
isPlay = false;
}
}//pause toggle end
} // init ends
//settings for window resize
function onWindowResize() {
camera.aspect = canvas_width / canvas_height;
camera.updateProjectionMatrix();
renderer.setSize( canvas_width, canvas_height );
} //window resize settings end
//animate function
function animate() {
if (!isPlay) return;
requestAnimationFrame(animate);
if ( plate !== undefined) {
plate.rotation.y += -.001;
};
if ( plateTwo !== undefined ) {
plateTwo.rotation.y += -.001;
};
render();
controls.update();
} //animate function end
//render
function render() {
renderer.render( scene, camera );
} //render end
You are not adding your renderer to the div.
you have this:
container = document.getElementById( 'myCanvas' );
document.body.appendChild( container );
// then later..
document.body.appendChild( renderer.domElement );
You code is...
getting a reference to myCanvas,
Moving myCanvas div to be the last element in the body,
Then adding the renderer canvas as the last element in the body (which will be after the myCanvas div).
What you probably want it this:
// Get a reference to the container div
container = document.getElementById( 'myCanvas' );
// Add the renderer.domElement, which is a canvas, to the container div.
container.appendChild( renderer.domElement );
Hey if anybody can please help me animate this object :
Up - Down To Move |
Left - Right to Rotate |
a to look up |
z to look down
Link to orignal file : http://www.aacctrust.org/anim/anim.htm
JSON Model File (with animation tail-wag):
http://wikisend.com/download/654748/wolf.json
The problem is the json model file has been provided with necessary details regarding the animation of the tail. The tail of the wolf is suppose to wag.
But it doesnot , I have tried everything but to no help .
Is something wrong in the code or am I not exporting properly frm Blender.
Below is the code :
<html>
<head>
<style>
body { margin: 0; }
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script src="three.js"></script>
<script>
var scene = new THREE.Scene();
var canvas = document.getElementById("myCanvas");
renderer = new THREE.WebGLRenderer({ canvas: canvas });
var camera = new THREE.PerspectiveCamera( 75, (canvas.width / canvas.height) , 1, 10000 );
var clock = new THREE.Clock(),mixer;
var forest,wolf,animation;
var fwd,bck,lft,rgt,up,down = 0 ;
var action = {};
camera.fov *= 0.3;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight);
document.body.appendChild( renderer.domElement );
renderer.setClearColor (0x22222222, 1);
camera.position.z = 28;
var material = new THREE.MeshPhongMaterial( { map: THREE.ImageUtils.loadTexture('wolf_uvcons.png') } );
// Wolf Texture Above # Forest Uses Local Materials #Blender
var loader = new THREE.JSONLoader();
loader.load('forest.json', function(geometry, materials) {
forest = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
forest.scale.x = forest.scale.y = forest.scale.z = 0.25;
forest.rotateX( Math.PI / 2 );forest.rotateZ( Math.PI ); //load :env:
forest.translation = THREE.GeometryUtils.center(geometry);
scene.add(forest);
});
loader.load('wolf.json', function(geometry, materials) {
wolf = new THREE.SkinnedMesh(geometry,material);
wolf.scale.x = wolf.scale.y = wolf.scale.z = 0.25; //load wolf
wolf.translation = THREE.GeometryUtils.center(geometry);
scene.add(wolf);
wolf.add(camera);
wolf.translateY(-27); /// Bring it down
wolf.translateZ(100); /// Bring it down
action.idle = new THREE.AnimationAction(geometry.animations[ 0 ]);
action.idle.weight = 1;
// Create animation mixer and pass object to it
mixer = new THREE.AnimationMixer(wolf);
mixer.addAction( action.idle );
});
var light = new THREE.AmbientLight(0xFFFFFFFF,1);
scene.add(light);
var pointLight = new THREE.PointLight( 0xffcccc,0.41 );
pointLight.position.set( 0, 100, 3 );
scene.add( pointLight );
var pointLight1 = new THREE.PointLight( 0xff0000,0.81 );
pointLight1.position.set( 100, 200, 3 );
scene.add( pointLight1 );
function moveWolf()
{
if (fwd==1)
{ wolf.translateZ(-1);}
if (bck==1)
{ wolf.translateZ(1);}
if (lft==1)
{ wolf.rotateY(Math.PI/200)}
if (rgt==1)
{ wolf.rotateY(-Math.PI/200);}
if (up==1)
{ camera.rotateX(Math.PI/200);}
if (down==1)
{ camera.rotateX(-Math.PI/200);}
}
function animate() {
requestAnimationFrame( animate );
moveWolf();
render();
var delta = clock.getDelta();
var theta = clock.getElapsedTime();
if ( mixer ) { mixer.update( delta ); }
}
function render() {
renderer.render( scene, camera );
}
animate();
</script>
<script>
document.onkeydown = checkKey1;
document.onkeyup = checkKey2;
function checkKey1(e) {
e = e || window.event;
if (e.keyCode == '38') {
// up arrow
fwd=1;
}
else if (e.keyCode == '40') {
// down arrow
bck=1;
}
else if (e.keyCode == '37') {
// left arrow
lft=1;
}
else if (e.keyCode == '39') {
// right arrow
rgt=1;
}
else if (e.keyCode == '65') {
up=1;
}
else if (e.keyCode == '90') {
down=1;
}
}
function checkKey2(e) {
e = e || window.event;
if (e.keyCode == '38') {
// up arrow
fwd=0;
}
else if (e.keyCode == '40') {
// down arrow
bck=0;
}
else if (e.keyCode == '37') {
// left arrow
lft=0;
}
else if (e.keyCode == '39') {
// right arrow
rgt=0;
}
else if (e.keyCode == '65') {
// right arrow
up=0;
}
else if (e.keyCode == '90') {
// right arrow
down=0;
}
}
</script>
</body>
</html>
UPDATE :
During the process of waiting for a reply I went on a self-exploration trip ;)
And found a nearly acceptable solution , steps are aa follows :
File >> Export >> Wavefront (OBJ)
Ensure that Animation is checked as option .
Click export (while exporting plz do it in a separate folder as no of files could be large depending on the number of frames.)
Copy the folder in Blender > Python > Bin directory.
Copy convertor from three.js utils OBJ for python3 to Blender > Python > Bin directory.
Launch command prompt in Admin mode and run the following command :
python objtothreejs_conv_python3 -i meshdirname\meshname_000001.obj -o output.js -m "meshdirname\meshname_*.obj"
Output Animation would be morph animation and would require some changes in the mixer for playing MorphAnims.
I have the following files( using last PIXI.js version):
index.html
<!DOCTYPE HTML>
<html>
<head>
<title>pixi.js example 1</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #000000;
width:100%;
height:100%;
}
canvas {
display:block;
}
</style>
<script src="js/pixi.js"></script>
<script src="js/XALUI/XALUI.js"></script>
<script src="js/XALUI/Button.js"></script>
</head>
<body>
<script>
// create an new instance of a pixi stage
var stage = new PIXI.Stage(0x66FF99);
// create a renderer instance
var renderer = PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight);
// add the renderer view element to the DOM
document.body.appendChild(renderer.view);
requestAnimFrame( animate );
// create a texture from an image path
var texture = PIXI.Texture.fromImage("bunny.png");
// XALUI
var button = new XALUI.Button(texture);
stage.addChild(button);
function animate() {
button.position.x = button.position.x + 20;
// render the stage
renderer.render(stage);
requestAnimFrame( animate );
}
</script>
</body>
</html>
js/XALUI/Button.js
XALUI.Button = function(texture) {
PIXI.DisplayObject.call(this);
this._button = new PIXI.Sprite(texture);
}
XALUI.Button.prototype = Object.create(PIXI.DisplayObject.prototype);
XALUI.Button.prototype.constructor = XALUI.Button;
XALUI.Button.prototype._renderWebGL = function(renderSession) {
this._button._renderWebGL(renderSession);
}
XALUI.Button.prototype._renderCanvas = function(renderSession) {
this._button._renderCanvas(renderSession);
};
How can I move my button to another position or to an another initial position? I have tried setting the position.x:
var button = new XALUI.Button(texture);
button.position.x = 100;
And also tried to set the position of the sprite:
this._button = new PIXI.Sprite(texture);
this._button.position.x = 100;
But it doesn't work. Please help.
The problem lies in that you're attaching and moving XALUI.Button instead of the inner PIXI.Sprite. Setting the position on the inner sprite (this._button.position.x) would only work if you had added that PIXI.Sprite to the stage. Since DisplayObjects do not contain multiple DisplayObjects, the stage has no reference to the actual sprite. There are a couple of ways to fix this. First, you could inherit from PIXI.Sprite
XALUI.Button = function(texture) {
PIXI.Sprite.call(this, texture);
};
XALUI.Button.prototype = Object.create(PIXI.Sprite.prototype);
XALUI.Button.prototype.constructor = XALUI.Button;
Or to have it inherit from DisplayObjectContainer in the same manner as above but that implies a button will hold multiple graphical objects so do what works best for you. Here's a working example of the first approach.
var XALUI = {};
// Inherits from PIXI.Sprite
XALUI.Button = function(texture) {
PIXI.Sprite.call(this, texture);
};
XALUI.Button.prototype = Object.create(PIXI.Sprite.prototype);
XALUI.Button.prototype.constructor = XALUI.Button;
// Prepare the stage
var stage = new PIXI.Stage(0x66FF99);
var renderer = PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight);
var texture = PIXI.Texture.fromImage("http://www.goodboydigital.com/pixijs/examples/1/bunny.png");
var button = new XALUI.Button(texture);
document.body.appendChild(renderer.view);
requestAnimationFrame(animate);
stage.addChild(button);
// Animate the button
function animate() {
button.position.x = button.position.x + 5;
if (button.position.x > window.innerWidth + button.width) {
button.position.x = -button.width;
}
renderer.render(stage);
requestAnimationFrame(animate);
}
<script src="https://cdn.rawgit.com/GoodBoyDigital/pixi.js/v2.0.0/bin/pixi.js"></script>