Animated mesh does not load in Babylon.js - javascript

I created a mesh in Blender and called it "Walking.babylon". I set it up in my code just how it states in the babylon tutorial:
///<reference path="/ref script/babylon.1.14-debug.js"/>
"use strict"
var canvas;
var engine;
var scene;
document.addEventListener("DOMContentLoaded", startBabylonJS, false);
function startBabylonJS() {
if (BABYLON.Engine.isSupported()) {
canvas = document.getElementById("renderCanvas");
engine = new BABYLON.Engine(canvas, true);
scene = new BABYLON.Scene(engine);
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
light.position = new BABYLON.Vector3(20, 150, 70);
//create the camera that will view our scene
var cam = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
cam.setTarget(new BABYLON.Vector3.Zero());
cam.attachControl(canvas, false);
scene.ambientColor = new BABYLON.Color3(0.3, 0.3, 0.3);
// Ground
var ground = BABYLON.Mesh.CreateGround("ground", 1000, 1000, 1, scene, false);
var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.2, 0.2);
groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
ground.material = groundMaterial;
ground.receiveShadows = true;
// Shadows
var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
// Dude
BABYLON.SceneLoader.ImportMesh("man", "Scenes/Models/Animation/", "Walking.babylon", scene, function (newMeshes, particleSystems, skeletons) {
var dude = newMeshes[0];
for (var index = 0; index < newMeshes.length; index++) {
shadowGenerator.getShadowMap().renderList.push(newMeshes[index]);
}
dude.rotation.y = Math.PI;
dude.position = new BABYLON.Vector3(0, 0, -80);
scene.beginAnimation(skeletons[0], 0, 100, true, 1.0);
});
//Once the scene is loaded, just register a render loop to render it
engine.runRenderLoop(function () {
scene.render();
});
//Resize
window.addEventListener("resize", function () {
engine.resize();
});
}
}
But When I load my scene and hit F12 I get this message in the console: Failed to load resource: the server responded with a status of 404 (Not Found) and right next to it is this link: http://localhost:50207/Scenes/Models/Animation/Walking.babylon.manifest?1419869394361
So my question is: what am I doing wrong that is causing my animated mesh not to show?

Open notepad and put this:
{
"version" : 1,
"enableSceneOffline" : true,
"enableTexturesOffline" : true
}
Then rename it: Walking.babylon.manifest
And put it where "Walking.babylon" is.

Related

Javascript/Three.js one million iterations loop performance

I have this javascript code that i need to run once for generating a grid of quadrants from a 3d object imported from blender (it is a three.js project).
The problem is that it runs a loop of about 1 million iterations and happens that the script takes a long time and then crashes.
To be precise, there are 10,000 iterations in which, each time, a mesh is generated and then a further loop of 121 raycaster.intersectObjects is executed.
What I would like to know if this script is really so demanding for an Intel i7-7800x CPU or if the problem depends on something else, such as browser memory or setting (I tested with all the browsers).
var scene= new THREE.Scene();
renderer= new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor (0xf9f9f9, 1);
renderer.gammaOutput= true;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled= true;
renderer.shadowMap.type= THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
var light= new THREE.DirectionalLight(0xffffff, 1.4, 100);
light.position.set(3, 2, 0);
light.castShadow= true;
scene.add(light);
light.shadow.mapSize.width= 512;
light.shadow.mapSize.height= 512;
light.shadow.camera.near= 0.5;
light.shadow.camera.far= 500;
var camera= new THREE.PerspectiveCamera(75,
window.innerWidth/window.innerHeight, 0.1, 100000);
camera.position.set(0, 70, 70);
camera.lookAt(0, 0, 0);
var controls= new THREE.OrbitControls(camera);
var loader= new THREE.GLTFLoader();
loader.load(
'http://localhost/planegeometryeditor/meshes/map/map_def.gltf',
function(gltf) {
gltf.scene.receiveShadow= true;
scene.add(gltf.scene);
scene.children[1].rotation.y= -(Math.PI/2);
var sceneobjs= scene.children[1].children;
for(var iobj in sceneobjs) {
var obj= sceneobjs[iobj];
obj.updateMatrixWorld();
if(obj.name== 'terrain') {
var terrain= obj;
for(var tmindex in terrain.children[0].children) {
terrain.children[0].children[tmindex].material.side=
THREE.DoubleSide;
}
}
if(obj.name.search('tree')!= -1) { obj.name= 'tree' }
}
var terrain_bbox = new THREE.Box3().setFromObject(terrain);
var terrain_position= terrain.position;
//------------ LOOP -----------------------
//-----------------------------------------
var idquad= 0;
for(var i= -49.5; i<=49.5; i= i+1) {
for(var ii= -49.5; ii<=49.5; ii= ii+1) {
var idquad= idquad+1;
var quadgeo= new THREE.PlaneGeometry(1,1);
var quadmat= new
THREE.MeshBasicMaterial({color:0x00ff00, side:
THREE.DoubleSide});
var quad= new THREE.Mesh(quadgeo, quadmat);
quad.name= 'quad-'+idquad;
scene.add(quad);
quad.rotation.x= -(Math.PI/2);
quad.position.x= i;
quad.position.y= -5;
quad.position.z= ii;
for(var a= (i-0.5); a<=(i+0.5); a=
Number((a+0.1).toFixed(2))) {
for(var aa= (ii-0.5); aa<=(ii+0.5); aa=
Number((aa+0.1).toFixed(2))) {
var raycaster= new THREE.Raycaster();
var rorigin= new THREE.Vector3(a, -5, aa);
var rdirection= new THREE.Vector3(0, 1, 0);
raycaster.set(rorigin, rdirection);
var intersects=
raycaster.intersectObjects(scene.children,
true);
}
}
}
}
//------------ LOOP -----------------------
//-----------------------------------------
}, // success
undefined,
function (error) { console.error(error); }
);
render();

How to load all models with the same size and the same camera position?

I'm setting up a website that loads 3d models in ply format, and I'm using Three.js with his PLYLOADER to do that.
My problem is that every model is loading in a different position. I've managed to make them load in a perpendicular way with the ground, but some of them are looking, some are looking far away from the camera and others look like they turn around a Y axis (not their center).
This is my HTML div:
<div id="WebGL-output"></div>
This is my JS code:
<script type="text/javascript">
var lesson7 = {
scene: null,
camera: null,
renderer: null,
container: null,
controls: null,
clock: null,
stats: null,
init: function() { // Initialization
// create main scene
this.scene = new THREE.Scene();
this.scene.fog = new THREE.FogExp2(0xcce0ff, 0.0003);
var SCREEN_WIDTH = '800',
SCREEN_HEIGHT = '600';
// prepare camera
var VIEW_ANGLE = 60, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
this.scene.add(this.camera);
this.camera.position.set(0, 0, 300);
this.camera.lookAt(new THREE.Vector3(0,0,0));
// prepare renderer
this.renderer = new THREE.WebGLRenderer({ antialias:true });
this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
this.renderer.setClearColor(this.scene.fog.color);
this.renderer.shadowMapEnabled = true;
this.renderer.shadowMapSoft = true;
// prepare container
this.container = document.getElementById('WebGL-output');
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 = 20000;
// prepare clock
this.clock = new THREE.Clock();
this.scene.add( new THREE.AmbientLight(0xFFFFFF) );
// light
var dirLight = new THREE.DirectionalLight(0x606060);
dirLight.position.set(200, 200, 1000).normalize();
this.camera.add(dirLight);
this.camera.add(dirLight.target);
// load models
this.loadModels();
},
loadModels: function() {
// prepare PLY loader and load the model
var scene = new THREE.Scene();
var oPlyLoader = new THREE.PLYLoader();
oPlyLoader.load("../models/{{ $analysis->three_d_model }}", function(geometry) {
var material = new THREE.MeshPhongMaterial( { color: 0xAAAAAA, specular: 0x111111, shininess: 0 } );
var meshMaterial = material;
// if (geometry.hasColors) { // to fix when models will have colors
meshMaterial = new THREE.MeshPhongMaterial({ opacity: 1, vertexColors: THREE.VertexColors });
// }
var mesh = new THREE.Mesh(geometry, meshMaterial);
// Solution: center mesh to world origin
mesh.geometry.computeBoundingBox();
var bbox = mesh.geometry.boundingBox;
var centerVec = new THREE.Vector3();
bbox.getCenter(centerVec); // get center of bbox into centerVec
// move mesh so that center of bbox is placed to world origin
mesh.applyMatrix(new THREE.Matrix4().makeTranslation(-centerVec.x, -centerVec.y, -centerVec.z));
// proceed doing stuff
// mesh.position.set(0, -6, 0);
// mesh.scale.set(5, 5, 5);
lesson7.scene.add(mesh);
});
}
};
// Animate the scene
function animate() {
requestAnimationFrame(animate);
render();
update();
}
// Update controls and stats
function update() {
lesson7.controls.update(lesson7.clock.getDelta());
// lesson7.stats.update();
}
// Render the scene
function render() {
if (lesson7.renderer) {
lesson7.renderer.render(lesson7.scene, lesson7.camera);
}
}
// Initialize lesson on page load
function initializeLesson() {
lesson7.init();
animate();
}
if (window.addEventListener)
window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;
</script>
I need help to make them all load with the same camera position, looking centred and in the same size.
PS: If i can have better results loading in different extension, I'm up for it.
Thanks in advance
EDIT:
I updated the code with the help of #alex. Now my models are very well centered. The only thing missing now si a fixed camera position however the model was saved
Currently see two solutions here ( if i understand correctly )
You objects are not centered correctly. The solution would be to open the model files in a 3d editor and set them all to the same size and make their center points consistent.
Once you load the model add it to an Object3D and find its center.
Docs about that here: https://threejs.org/docs/#api/en/core/Object3D
the below code this.objectAll is the Object3D. Which is put into a box and we make the controls look at the center of that box. If all model are consistent then this should work.
this.boundingBox = new THREE.Box3();
this.boundingBox.setFromObject(this.objectAll);
this.boundingBox.center(this.controls.target);
Best of luck!

Changing color of material in babylon.js

I am just getting into babylon.js, but I can't seem to figure out, how would you change color of a material ?
My code currently is:
/* eslint-disable */
import * as BABYLON from 'babylonjs';
// Get the canvas element from our HTML above
const canvas = document.getElementById("root");
// Load the BABYLON 3D engine
const engine = new BABYLON.Engine(canvas, true);
let fn;
let mainColor = new BABYLON.Color3(1.0, 0.2, 0.7);
setTimeout(() => {
mainColor = new BABYLON.Color3(0.3, 0.2, 0.2);
fn();
}, 2000);
// This begins the creation of a function that we will 'call' just after it's built
function createScene () {
// Now create a basic Babylon Scene object
const scene = new BABYLON.Scene(engine);
// Change the scene background color to green.
scene.clearColor = new BABYLON.Color4(0.5, 0.8, 0.6, 0.8);
// This creates and positions a free camera
const camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 1, 0.8, 30, new BABYLON.Vector3(0, 0, 0), scene);
// This targets the camera to scene origin
camera.setTarget(BABYLON.Vector3.Zero());
// This attaches the camera to the canvas
camera.attachControl(canvas, false);
// This creates a light, aiming 0,1,0 - to the sky.
const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(1, 1, 1), scene);
// Dim the light a small amount
light.intensity = .5;
// Let's try our built-in 'sphere' shape. Params: name, subdivisions, size, scene
const sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
const materialSphere1 = new BABYLON.StandardMaterial("texture1", scene);
materialSphere1.alpha = 1;
materialSphere1.diffuseColor = mainColor;
sphere.material = materialSphere1;
sphere.position.y = 1;
// Move the sphere upward 1/2 its height
// Let's try our built-in 'ground' shape. Params: name, width, depth, subdivisions, scene
const ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
fn = () => {
materialSphere1.diffuseColor = mainColor;
}
// Leave this function
return scene;
}; // End of createScene function
const scene = createScene();
engine.runRenderLoop(function () {
scene.render();
});
window.addEventListener("resize", function () {
engine.resize();
});
It looks aweful to me. As you can see i am defining fn inside the createScene which then allows me to modify it that way, however I believe that there should be a better way of doing it. I tried creating a function outside the createScene(), that would fetch the color, and then use that for materialSphere1.diffuseColor, however that did not work. So my question is: Is there any other (nicer) way to change a color of a material in babylon.js
Why not declaring your material outside of the callback?
You can also use scene.materials to browse materials

Babylon.js - e.Gamepads is not a constructor

Just following along with a Babylon tutorial and it might be that its using an older version of BabylonJS - but I am getting the error:
e.Gamepads is not a constructor
with the following code
var bApp = bApp || {};
bApp.init = function(){
//get the canvas
var canvas = document.getElementById('renderCanvas');
//create a BabylonJS engine object, true for antialias
var engine = new BABYLON.Engine(canvas, true);
//create a scene
var scene = new BABYLON.Scene(engine);
//create a camera
var camera = new BABYLON.ArcRotateCamera('camera', 0, 0, 15, BABYLON.Vector3.Zero(), scene);
//let the user move the camera
camera.attachControl(canvas);
//light
var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), scene);
var sun = BABYLON.Mesh.CreateSphere('sun', 16, 4, scene);
engine.runRenderLoop(function () {
scene.render();
});
// the canvas/window resize event handler
window.addEventListener('resize', function(){
engine.resize();
});
}
window.addEventListener('DOMContentLoaded', function(){
bApp.init();
});
The tutorial seems to an older version of Babylon (2.1) and I am trying to use 2.4.

Using textures in THREE.js

I am starting with THREE.js, and I am trying to draw a rectangle with a texture on it, lit by a single source of light. I think this is as simple as it gets (HTML omitted for brevity):
function loadScene() {
var world = document.getElementById('world'),
WIDTH = 1200,
HEIGHT = 500,
VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000,
renderer = new THREE.WebGLRenderer(),
camera = new THREE.Camera(VIEW_ANGLE, ASPECT, NEAR, FAR),
scene = new THREE.Scene(),
texture = THREE.ImageUtils.loadTexture('crate.gif'),
material = new THREE.MeshBasicMaterial({map: texture}),
// material = new THREE.MeshPhongMaterial({color: 0xCC0000});
geometry = new THREE.PlaneGeometry(100, 100),
mesh = new THREE.Mesh(geometry, material),
pointLight = new THREE.PointLight(0xFFFFFF);
camera.position.z = 200;
renderer.setSize(WIDTH, HEIGHT);
scene.addChild(mesh);
world.appendChild(renderer.domElement);
pointLight.position.x = 50;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.addLight(pointLight);
renderer.render(scene, camera);
}
The problem is, I cannot see anything. If I change the material and use the commented one, a square appears as I would expect. Note that
The texture is 256x256, so its sides are power of two
The function is actually called when the body is loaded; indeed it works with a different material.
It does not work even if I serve the file from a webserver, so it is not an issue of cross-domain policy not allowing to load the image.
What I am I doing wrong?
By the time the image is loaded, the renderer has already drawn the scene, hence it is too late. The solution is to change
texture = THREE.ImageUtils.loadTexture('crate.gif'),
into
texture = THREE.ImageUtils.loadTexture('crate.gif', {}, function() {
renderer.render(scene);
}),
Andrea solution is absolutely right, I will just write another implementation based on the same idea.
If you took a look at the THREE.ImageUtils.loadTexture() source you will find it uses the javascript Image object. The $(window).load event is fired after all Images are loaded ! so at that event we can render our scene with the textures already loaded...
CoffeeScript
$(document).ready ->
material = new THREE.MeshLambertMaterial(map: THREE.ImageUtils.loadTexture("crate.gif"))
sphere = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, rings), material)
$(window).load ->
renderer.render scene, camera
JavaScript
$(document).ready(function() {
material = new THREE.MeshLambertMaterial({ map: THREE.ImageUtils.loadTexture("crate.gif") });
sphere = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, rings), material);
$(window).load(function() {
renderer.render(scene, camera);
});
});
Thanks...
In version r75 of three.js, you should use:
var loader = new THREE.TextureLoader();
loader.load('texture.png', function ( texture ) {
var geometry = new THREE.SphereGeometry(1000, 20, 20);
var material = new THREE.MeshBasicMaterial({map: texture, overdraw: 0.5});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
In version r82 of Three.js TextureLoader is the object to use for loading a texture.
Loading one texture (source code, demo)
Extract (test.js):
var scene = new THREE.Scene();
var ratio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight,
0.1, 50);
var renderer = ...
[...]
/**
* Will be called when load completes.
* The argument will be the loaded texture.
*/
var onLoad = function (texture) {
var objGeometry = new THREE.BoxGeometry(20, 20, 20);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
var mesh = new THREE.Mesh(objGeometry, objMaterial);
scene.add(mesh);
var render = function () {
requestAnimationFrame(render);
mesh.rotation.x += 0.010;
mesh.rotation.y += 0.010;
renderer.render(scene, camera);
};
render();
}
// Function called when download progresses
var onProgress = function (xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
};
// Function called when download errors
var onError = function (xhr) {
console.log('An error happened');
};
var loader = new THREE.TextureLoader();
loader.load('texture.jpg', onLoad, onProgress, onError);
Loading multiple textures (source code, demo)
In this example the textures are loaded inside the constructor of the mesh, multiple texture are loaded using Promises.
Extract (Globe.js):
Create a new container using Object3D for having two meshes in the same container:
var Globe = function (radius, segments) {
THREE.Object3D.call(this);
this.name = "Globe";
var that = this;
// instantiate a loader
var loader = new THREE.TextureLoader();
A map called textures where every object contains the url of a texture file and val for storing the value of a Three.js texture object.
// earth textures
var textures = {
'map': {
url: 'relief.jpg',
val: undefined
},
'bumpMap': {
url: 'elev_bump_4k.jpg',
val: undefined
},
'specularMap': {
url: 'wateretopo.png',
val: undefined
}
};
The array of promises, for each object in the map called textures push a new Promise in the array texturePromises, every Promise will call loader.load. If the value of entry.val is a valid THREE.Texture object, then resolve the promise.
var texturePromises = [], path = './';
for (var key in textures) {
texturePromises.push(new Promise((resolve, reject) => {
var entry = textures[key]
var url = path + entry.url
loader.load(url,
texture => {
entry.val = texture;
if (entry.val instanceof THREE.Texture) resolve(entry);
},
xhr => {
console.log(url + ' ' + (xhr.loaded / xhr.total * 100) +
'% loaded');
},
xhr => {
reject(new Error(xhr +
'An error occurred loading while loading: ' +
entry.url));
}
);
}));
}
Promise.all takes the promise array texturePromises as argument. Doing so makes the browser wait for all the promises to resolve, when they do we can load the geometry and the material.
// load the geometry and the textures
Promise.all(texturePromises).then(loadedTextures => {
var geometry = new THREE.SphereGeometry(radius, segments, segments);
var material = new THREE.MeshPhongMaterial({
map: textures.map.val,
bumpMap: textures.bumpMap.val,
bumpScale: 0.005,
specularMap: textures.specularMap.val,
specular: new THREE.Color('grey')
});
var earth = that.earth = new THREE.Mesh(geometry, material);
that.add(earth);
});
For the cloud sphere only one texture is necessary:
// clouds
loader.load('n_amer_clouds.png', map => {
var geometry = new THREE.SphereGeometry(radius + .05, segments, segments);
var material = new THREE.MeshPhongMaterial({
map: map,
transparent: true
});
var clouds = that.clouds = new THREE.Mesh(geometry, material);
that.add(clouds);
});
}
Globe.prototype = Object.create(THREE.Object3D.prototype);
Globe.prototype.constructor = Globe;
Without Error Handeling
//Load background texture
new THREE.TextureLoader();
loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg' , function(texture)
{
scene.background = texture;
});
With Error Handling
// Function called when download progresses
var onProgress = function (xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
};
// Function called when download errors
var onError = function (error) {
console.log('An error happened'+error);
};
//Function called when load completes.
var onLoad = function (texture) {
var objGeometry = new THREE.BoxGeometry(30, 30, 30);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
var boxMesh = new THREE.Mesh(objGeometry, objMaterial);
scene.add(boxMesh);
var render = function () {
requestAnimationFrame(render);
boxMesh.rotation.x += 0.010;
boxMesh.rotation.y += 0.010;
sphereMesh.rotation.y += 0.1;
renderer.render(scene, camera);
};
render();
}
//LOAD TEXTURE and on completion apply it on box
var loader = new THREE.TextureLoader();
loader.load('https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/The_Earth_seen_from_Apollo_17.jpg/1920px-The_Earth_seen_from_Apollo_17.jpg',
onLoad,
onProgress,
onError);
Result:
https://codepen.io/hiteshsahu/pen/jpGLpq/
Use TextureLoader to load a image as texture and then simply apply that texture to scene background.
new THREE.TextureLoader();
loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg' , function(texture)
{
scene.background = texture;
});
Result:
https://codepen.io/hiteshsahu/pen/jpGLpq?editors=0011
See the Pen Flat Earth Three.JS by Hitesh Sahu (#hiteshsahu) on CodePen.

Categories