onLoad in TextureLoader is not working? - javascript

Am using texture loader in Three.js, the onLoad function in it is not working gives an error in the console
var TextureLoader = new THREE.TextureLoader();
boxtexture.onChange(function (value) {
TextureLoader.load('textures/' + value,
// onLoad callback
function(texture){
Box1.material.map = texture
Box1.material.map.wrapT = THREE.RepeatWrapping;
Box1.material.map.wrapS = THREE.RepeatWrapping;
Box1.material.map.repeat.set(38, 38);
Box2.material.map = texture
Box2.material.map.wrapT = THREE.RepeatWrapping;
Box2.material.map.wrapS = THREE.RepeatWrapping;
Box2.material.map.repeat.set(38, 38);
},
// onProgress callback
function(loaded)
{
load = 1;
console.log(loaded);
window.open(Renderer.domElement.toDataURL());
}
);
});
But it doesn't call the onLoad function and gives an error message in console
The textures are loaded properly. But I need to call a function when it is loaded.

I just solve it using Loading manager in three.js
var manager = new THREE.LoadingManager();
manager.onLoad = function () {
// call back function when the texture gets loaded
}
var TextureLoader = new THREE.TextureLoader(manager);
TextureLoader.load('textures/', function (texture) {
material.map = texture;
});

Related

How to set texture dynamically on MeshPhongMaterial?

I'm trying to set the texture on THREE.MeshPhongMaterial but the texture does not load.
My code:
let earth_geometry = new THREE.SphereGeometry(450, 10, 10)
let earth_material = new THREE.MeshPhongMaterial({
emissive: 0xffffff
})
let earth = new THREE.Mesh(earth_geometry, earth_material)
loadImage(earth_material, '/img/earth.jpg')
scene.add(earth)
function loadImage(material, url) {
let texture = new THREE.TextureLoader().load(url, (e) => {
texture.minFilter = THREE.LinearFilter
texture.anisotropy = 8
material.map = texture
material.needsUpdate = true
})
If I change the material to THREE.MeshBasicMaterial() the texture is loaded.
Why does it not work with THREE.MeshPhongMaterial?
Might have something to do with your texture variable?
I think you are setting he texture variable, after you are trying to use it.
the e argument in the loader callback is actually the loaded texture.
Try this..
function loadImage(material, url) {
let loader = new THREE.TextureLoader()
loader.load(url, (texture) => {
texture.minFilter = THREE.LinearFilter
texture.anisotropy = 8
material.map = texture
material.needsUpdate = true
// maybe need this too..
material.map.needsUpdate = true;
})

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.

Threejs textureLoader - scaling and mapping to mesh

I have a cube that I'm trying to map an image onto. I'm loading the image with a loading Manager. I'm wondering why the material.map is returning as undefined, also wondering if maybe I have a scaling issue. The original image is 512x512. The boxes are 20x20x20.
I left out all the code about camera, renderer, etc., but I attempted to include it all in the code snippet/interactive part below.
var loadingManager = new THREE.LoadingManager();
loadingManager.onProgress = function (item, loaded, total) {
//Loading percentage
console.log(loaded / total * 100 + '%');
}
//Signify loading done
loadingManager.onLoad = function () {
//Start the animation when the models are done loading
animate();
}
function init() {
//create a loader
var loader2 = new THREE.TextureLoader(loadingManager);
//load the texture, and when it's done, push it into a material
loader2.load("../img/leo.jpg", function (texture) {
//do I need to do this?
texture.wrapS = texture.wrapT = THREE.RepeatWrapping
texture.repeat.set(boxSize, boxSize)
//why is this texture not coming through?
console.log(texture)
//does not work:
material1 = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide
});
})
var geo = new THREE.BoxGeometry(30, 30, 30)
var mat = new THREE.MeshBasicMaterial({
color: 0xb7b7b7
})
mesh = new THREE.Mesh(geo, material1)
scene.add(mesh)
}
// This works, so I know the image path is correct
var img = document.createElement('img');
img.src = '../img/leo.jpg';
document.getElementById('container').appendChild(img);
The value of texture from the console log is this:
The error is related to the fact that you are associating the material outside the callback of the load function of your loader, you have to do that inside the callback.
From the documentation for TextureLoader:
onLoad - Will be called when load completes.
You have to do as follow:
loader2.load("../img/leo.jpg", function(texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping
texture.repeat.set( boxSize,boxSize )
//why is this texture 1 not coming through?
console.log(texture)
//neither of these work:
var geo = new THREE.BoxGeometry(30,30,30);
material1 = new THREE.MeshBasicMaterial({ map: texture,side: THREE.DoubleSide });
var mesh = new THREE.Mesh(geo, material1);
// animation loop
function animate() {
requestAnimationFrame( animate );
render();
// update stats
stats.update();
}
...
});
To make this more readable and avoid a callback nightmare do something like this:
function myInit(texture) {
...
}
loader2.load("../img/leo.jpg", myInit);

Three.js update scale object-size via view

i'm new in three.js and have a problem by scaling a object. I already looked at the documentation (https://github.com/mrdoob/three.js/wiki/Updates), but i don't really understand it at all.
My Problem: By changing a HTML-select-element the CubeGeometry should be scaled in the x-direction. That is already working BUT the "old" Cube do not disappear. So i have 2 Cubes. But i want only one Cube with the current size. I hope you can understand my problem ;-)
So here is my Code from the View:
$(document).on('change',".configurator > form select",function(event){
// update 3D-object - is that a nice way???
$.getScript("/javascripts/3D-animation.js.erb", function(){
// here comes the update
OBJECT3D.updateObject();
});
})
And here is my 3D-animation.js.erb:
var drawing_area;
var renderer;
var camera;
var obj;
var scene;
var OBJECT3D = {};
$(function() {
// get drawing_area
drawing_area = document.getElementById("canvas_wrapper");
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(drawing_area.clientWidth, drawing_area.clientHeight);
renderer.setClearColor( 0xffffff, 1);
// add renderer to drawing_area
drawing_area.appendChild(renderer.domElement);
// initialize camera
camera = new THREE.PerspectiveCamera(45, drawing_area.clientWidth/drawing_area.clientHeight, 1, 100000);
camera.position.z = 1000;
camera.position.y = 100;
camera.position.x = 300;//-0.78;
// create texture
var texture = THREE.ImageUtils.loadTexture( "/images/materials/texture_1.jpg" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1, 1 );
// create object
var obj_form = new THREE.CubeGeometry(250,250,250);
var obj_material = new THREE.MeshLambertMaterial( { map: texture,ambient: 0x999999 } );
OBJECT3D.obj = new THREE.Mesh(obj_form, obj_material);
// so what do i need here?
OBJECT3D.obj.geometry.dynamic = true;
// OBJECT3D.obj.geometry.__dirtyVertices = true;
// OBJECT3D.obj.geometry.__dirtyNormals = true;
// OBJECT3D.obj.geometry.verticesNeedUpdate = true;
// create scene
scene = new THREE.Scene();
scene.add(camera);
scene.add(OBJECT3D.obj);
// create lights
pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 400;
pointLight.position.y = 200;
pointLight.position.z = 1300;
scene.add(pointLight);
ambientLight = new THREE.AmbientLight( 0xffffff);
scene.add( ambientLight );
requestAnimationFrame(render);
function render(){
requestAnimationFrame(render);
OBJECT3D.obj.rotation.y += 0.005;
OBJECT3D.obj.rotation.z += 0.005;
renderer.render(scene, camera);
};
// update object
OBJECT3D.updateObject = function () {
console.log("in update");
OBJECT3D.obj.scale.x = 2.5; // SCALE
OBJECT3D.obj.geometry.needsUpdate = true;
//render();
}
});
Sorry, if the Code is not the best one, but i'm really new in this stuff! :) hope you can help me!
Thanks!
I think it is because you are using $.getScript method. This will load your script each time again and create a new instance of OBJECT3D.
I propose that you make sure that your code in 3D-animation.js.erb will be included and called only once upon the load of a page (include it just like any other regular javascript file) and then call your update directly like this:
$(document).on('change',".configurator > form select", function(event) {
OBJECT3D.updateObject();
});
also, I believe you can drop following lines of code:
OBJECT3D.obj.geometry.needsUpdate = true;
and
OBJECT3D.obj.geometry.dynamic = true;

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