I was wondering if its possible to add an object that is loaded via THREE.JSONLoader as public variable? Ideally, I'd like all my loaded textures, material creation, and geometry to be public variables at the top of my script for easy manipulation (I have strong 3D background but new to js and webGL). I'm finding that my public vars that are declared something - are no longer public once they're added as a parameter to a function - in this case the JSONLoader. However, just naming a var, without declaring its value "runs" but I get weird THREE.min.js error I can't comprehend. I've included my code below - know it has other issues - please feel free to let me know how bad it is - it helps me learn :)
//webGL
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.set(0, 16, 25);
camera.rotation.x += -0.32;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
cubeCamera = new THREE.CubeCamera(1, 1000, 256); // parameters: near, far, resolution
cubeCamera.renderTarget.texture.minFilter = THREE.LinearMipMapLinearFilter; // mipmap filter
scene.add(cubeCamera);
///LOADERS
var loadTexture = new THREE.TextureLoader();
var loaderJs = new THREE.JSONLoader();
///TEXTURES
var skyTexture = loadTexture.load("textures/background.jpg");
var seatTexture = loadTexture.load("textures/abc_Diffuse.jpg");
///MATERIALS
var skyMaterial = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
map: skyTexture
});
var frameMaterial = new THREE.MeshLambertMaterial({
//envMap: cubeCamera.renderTarget,
color: 0xffffff
});
var seatMaterial = new THREE.MeshLambertMaterial({
map: seatTexture
});
///GEOMETRY and MESHES
var frameGeo;
var skyGeo = new THREE.SphereGeometry(30, 30, 30);
var skySphere = new THREE.Mesh(skyGeo, skyMaterial);
scene.add(skySphere);
loaderJs.load("models/stoolFrame.js", function (){
frameGeo = new THREE.Mesh(frameGeo, frameMaterial);
frameGeo.scale.set(.5, .5, .5);
barStool.add(frameGeo);
});
loaderJs.load("models/stoolSeat.js", function (seatGeo){
seatGeo = new THREE.Mesh(seatGeo, seatMaterial);
seatGeo.scale.set(.5, .5, .5);
barStool.add(seatGeo);
});
var barStool = new THREE.Object3D();
scene.add(barStool);
var render = function () {
requestAnimationFrame(render);
barStool.rotation.y += 0.01;
frameGeo.visible = false;
cubeCamera.position.copy(frameGeo.position);
cubeCamera.updateCubeMap(renderer, scene);
frameGeo.visible = true;
renderer.render(scene, camera);
};
render();
When loading your stoolFrame, you forgot to add the parameters for the callback function, check the documentation example. The callback function takes a geometry and a material. When loading the stoolSeat you forgot the material aswell (provided you have a material for the stool)
var frameGeometry;
var frameMaterial;
var frameMesh;
loaderJs.load("models/stoolFrame.js", function (geometry, material){
frameGeometry = geometry;
frameMaterial = material
frameMesh = new THREE.Mesh(frameGeometry, frameMaterial);
frameMesh.scale.set(.5, .5, .5);
barStool.add(frameMesh);
});
Above is an example on how to make the geometry, material and mesh into global variables. However you probably dont need to save the geometry and material as global vars (you can access them from the mesh anyway: frameMesh.material; frameMesh.geometry).
If the parameter names are the same as a global variable name, javascript will use the parameter variable instead of the global one when trying to access it.
Related
I'm using three.js for an AR application to show a 3D object (exported from Maya with maya exporter to threejs) on a marker detected using JSARToolKit.
It all works fine on localhost(I see texture), but when I upload it online (github) and I see the preview object appears all black without texture (like if I remove the light).
// load the model
var loader = new THREE.JSONLoader;
var object;
//var geometry = new THREE.BoxGeometry(1, 1, 1);
loader.load('js/object3d.js', function(geometry, materials){
var material = new THREE.MeshFaceMaterial(materials);
object = new THREE.Mesh(geometry, material);
container.add(object);
});
var ambLight = new THREE.AmbientLight( 0x909090, 2.0 );
container.add( ambLight );
Any ideas?
I am not sure if this is the problem, but it might be that your loader runs into some error but since you didn't set an error handler for your loader you don't get notified.
The JSONLoader load method takes four arguments (url, onLoad, onProgress, onError) as you can see here in the class on line 40.
Try to set an error handler (onError callback method) and see what you get. For example:
var onload = function(geometry, materials){
var material = new THREE.MeshFaceMaterial(materials);
object = new THREE.Mesh(geometry, material);
container.add(object);
});
var onProgress = function(){
// your optional on progress logic
}
var onError = function(error){
console.log( error );
}
var loader = new THREE.JSONLoader;
loader.load('js/object3d.js', onLoad, onProgress, onError);
I'm using three.js and the attached code is returning the following error
Uncaught TypeError: THREE.Scene is not a function
init # new 1.html:18
on this line
scene=new THREE.Scene();
What could be causing this and how can I go about fixing it?
window.onload = init
var scene, camera, render;
function init() {
container = document.createElement('div');
document.body.appendChild(container);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 200); //перспективная проекция
camera.position.y = 150;
camera.position.z = 500;
var line_geometry = new THREE.Geometry();
line_geometry.vertices.push(new THREE.Vector3(0, 0, 0));
line_geometry.vertices.push(new THREE.Verctor3(0, 40, 50));
var line = new THREE.Line(line_geometry);
scene.add(line);
render = new THREE.WebGLRender(); //рендеринг
render = setSize(window.innerWidth, window.innerHeight); //инициализация рендеринга
container.appendChild(render.domElement); //??
render.render(scene);
}
Here is a screen shot of the error.
You have many typos in your code:
Verctor3 instead of Vector3
WebGLRender instead of WebGLRenderer
render.render(scene); instead of render.render(scene, camera);
and so on.
Also you should just run init() at start instead of window.onload=init.
Generally - please refer to this page. There are many working examples there.
I have a 3D object of a chair made in Blender and exported as a .obj and .mtl. First of all, the load time is a horrendous 40+ seconds; I have no idea why (Visual Studio possibly?) Second, the images textures are not loading properly. With no ambient or directional lighting I get a silhouette. With the lighting I get a slight hint of gray to give a little depth but nothing close to the many colors in the original object. I have only been working with ThreeJS for a few days now so I'm quite new to it. Hence, I am at a loss. I have read several artciles related to my issue but none seem to solve the problem. I even went so far as to add an addon to Blender to export to .js. I could not get it to even load properly (I assume it had to do with the fact I kept the same loader info from the .OBJMTLLoader and the .JSONLoader doesn't support or needs more than what I gave; I just am not familiar enough with it know). Any ideas/suggestions? Here is my script:
`
<script src="js/three.min.js"></script>
<script src="js/controls/TrackballControls.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script src="js/loaders/OBJMTLLoader.js"></script>
<script src="js/Detector.js"></script>
<script>
if (!Detector.webgl) Detector.addGetWebGLMessage();
var container;
var camera, controls, scene, renderer;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.01, 1e10);
camera.position.z = 2;
//controls
controls = new THREE.TrackballControls(camera);
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;
controls.panSpeed = 2;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
scene = new THREE.Scene();
scene.add(camera);
//lights
var ambient = new THREE.AmbientLight(0xCCCCCC);
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight(0xCCCCCC);
directionalLight.position.set(0, 0, 2).normalize();
scene.add(directionalLight);
//main img
var material = new THREE.MeshBasicMaterial({ color: '0xCCCCCC' });
var loader = new THREE.OBJMTLLoader();
loader.addEventListener('load', function (event) {
var geometry = event.content;
//var mesh = new THREE.Mesh(geometry);
scene.add(geometry, material);
});
loader.load('chair.obj', 'chair.mtl');
// renderer
renderer = new THREE.WebGLRenderer({ antialias: false });
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
controls.handleResize();
}
function animate() {
requestAnimationFrame(animate);
render();
controls.update();
}
function render() {
var timer = Date.now() * 0.0005;
renderer.render(scene, camera);
}
</script>
`
Well, you are defining a gray MeshBasicMaterial to use with your obj instead of using the material(s) that the loader will create for you, i guess.
Just use this. The "object" should have the material defined in your mtl-file.
loader.addEventListener( 'load', function ( event ) {
var object = event.content;
scene.add( object );
});
loader.load( 'chair.obj', 'chair.mtl' );
Concerning your load time: Could be your local server. Also, be aware that the obj file format ist quite big in raw ascii format. Also, if the mtl-file defines textures, they are maybe quite big in file size, too.
#GuyGood was correct in my case for the long load time. I was using the python simplehttpserver and switch to a node server and no more slow texture loading problems. Check out the threejs wiki for other local server options.
I'm trying to treat layers as pages -- i.e. I draw on one page, then turn the page and draw on another, each time storing the previous page in case the user goes back to it.
In my mind this translates as:
Create current_layer global pointer.
Each time newPage() is called, store the old layer in an array, and overwrite the pointer
layer_array.push(current_layer); //store old layer
current_layer = new Kinetic.Layer(); //overwrite with a new
New objects are then added to the current_layer which binds them to the layer, whether they are drawn or not. (e.g. current_layer.add(myCircle) )
Retrieving a page is simply updating the pointer to the requesting layer in the array, and redrawing the page. All the child nodes attached to the layer will also be drawn too
current_layer = layer_array[num-1]; //num is Page 2 e.g
current_layer.draw()
However nothing is happening! I can create new pages, and store them appropriately - but I cannot retrieve them again...
Here's my full code (my browser is having problems using jsfiddle):
<html>
<head>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.0.min.js"></script>
<script>
//Global
var stage; //canvas
var layer_array = [];
var current_page; //pointer to current layer
window.onload = function() {
stage = new Kinetic.Stage({
container: 'container',
width: 400,
height: 400
});
//Add initial page to stage to draw on
newPage()
};
//--- Functions ----//
function newPage(){
if(!current_page){
console.log("current page undefined");
} else {
layer_array.push(current_page);
// stage.remove(current_page);
//Nope, not working.
stage.removeChildren();
//Works, but I think it unbinds all objects
// from their specific layers...
// stage.draw()
console.log("Stored layer and removed it from stage");
}
current_page = new Kinetic.Layer();
console.log("Currently on page:"+(layer_array.length+1));
stage.add(current_page);
stage.draw();
}
function gotoPage(num){
stage.removeChildren()
stage.draw()
num = num-1;
if(num >= 0) {
current_page = layer_array[num];
console.log("Now on page"+(num+1));
stage.add(current_page);
stage.draw();
}
}
function addCircletoCurrentPage()
{
var rand = Math.floor(3+(Math.random()*10));
var obj = new Kinetic.Circle({
x: rand*16, y: rand*16,
radius: rand,
fill: 'red'
})
var imagelayer = current_page;
imagelayer.add(obj);
imagelayer.draw();
}
</script>
</head>
<body>
<div id="container"></div>
<button onclick="addCircletoCurrentPage()" >click</button>
<button onclick="newPage()" >new</button>
<button onclick="gotoPage(1)" >page1</button>
<button onclick="gotoPage(2)" >page2</button>
<button onclick="gotoPage(3)" >page3</button>
</body>
</html>
This was a fun problem. I think this fixes your troubles: http://jsfiddle.net/LRNHk/3/
Basically, you shouldn't remove() or removeChildren() as you risk de-referencing them.
Instead you should use:
layer.hide(); and layer.show();
this way, you keep all things equal and you get speedy draw performance.
So your go to page function should be like this:
function gotoPage(num){
for(var i=0; i<layer_array.length; i++) {
layer_array[i].hide();
}
layer_array[num].show();
console.log("Currently on page:"+(num));
console.log("Current layer: " + layer_array[num].getName());
stage.draw();
}
I also modified your other functions, which you can see in the jsfiddle.
Okay I changed my approach and instead of swapping layers (100x easier and makes more sense), I instead opted for serializing the entire stage and loading it back.
It works, but it really shouldn't have to be like this dammit
//Global
var stage; //canvas
var layer_array = [];
var current_page; //pointer to current layer
var page_num = 0;
window.onload = function() {
stage = new Kinetic.Stage({
container: 'container',
width: 400,
height: 400
});
//Add initial page to stage to draw on
newPage()
};
//--- Functions ----//
function newPage(){
if(!current_page){
console.log("current page undefined");
} else {
savePage(page_num)
stage.removeChildren()
console.log("Stored layer and removed it from stage");
}
current_page = new Kinetic.Layer();
console.log("Currently on page:"+(layer_array.length+1));
stage.add(current_page);
stage.draw();
page_num ++;
}
function savePage(num){
if( (num-1) >=0){
var store = stage.toJSON();
layer_array[num-1] = store;
console.log("Stored page:"+num)
}
}
function gotoPage(num){
savePage(page_num);
stage.removeChildren()
if(num-1 >= 0) {
var load = layer_array[num-1];
document.getElementById('container').innerHTML = ""; //blank
stage = Kinetic.Node.create(load, 'container');
var images = stage.get(".image");
for(i=0;i<images.length;i++)
{
//function to induce scope
(function() {
var image = images[i];
var imageObj = new Image();
imageObj.onload = function() {
image.setImage(imageObj);
current_page.draw();
};
imageObj.src = image.attrs.src;
})();
}
stage.draw();
page_num =num //update page
}
}
function addCircletoCurrentPage()
{
var rand = Math.floor(3+(Math.random()*10));
var obj = new Kinetic.Circle({
x: rand*16, y: rand*16, name: "image",
radius: rand,
fill: 'red'
})
var imagelayer = current_page;
imagelayer.add(obj);
imagelayer.draw();
}
There are some threads about textures which do not showing up. I have tried them all, but nothing helped.
I have spent a few hours on this now. Every time I end up looking at a black sphere.
I am working on Chrome v18 and Windows 7. I also tried Firefox, but this browser does not really support Three.js.
This is the body of the script:
<body>
<script src="../build/Three.js"></script>
<script src="js/Stats.js"></script>
<script src="../build/jquery-1.7.2.min.js"></script>
This is the script itself:
// stap1) camera, set the scene size
var WIDTH = 400,
HEIGHT = 300;
// set some camera attributes
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR );
// stap2) scene:
var scene = new THREE.Scene();
// the camera starts at 0,0,0 so pull it back
scene.add(camera);
camera.position.z = +300;
// get the DOM element to attach to
// - assume we've got jQuery to hand
var container = $('#container');
// stap3)create a WebGL renderer:
var renderer = new THREE.WebGLRenderer();
// start the renderer
renderer.setSize(WIDTH, HEIGHT);
// attach the render-supplied DOM element
container.append(renderer.domElement);
// bol maken:
// create the sphere's material
// b.v: THREE.MeshBasicMaterial
var sphereMaterial = new THREE.MeshLambertMaterial(
{
map: THREE.ImageUtils.loadTexture("http://dev.root.nl/tree/examples/textures/ash_uvgrid01.jpg")
});
// set up the sphere vars
var radius = 50, segments = 16, rings = 16;
var sphereGeometry = new THREE.SphereGeometry(radius, segments, rings);
// create a new mesh with sphere geometry -
var sphere = new THREE.Mesh(
sphereGeometry,
sphereMaterial
);
sphere.position.x=0;
var s=1;
sphere.scale.set(s, s, s);
// add the sphere to the scene
scene.add(sphere);
// create a point light
var pointLight = new THREE.PointLight( 0xFFFFFF );
// set its position
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
// add to the scene
scene.add(pointLight);
// draw!
renderer.render(scene, camera);
You need to wait until the image used as texture is fully downloaded.
I have put your code on the web: http://jsfiddle.net/4Qg7K/ and just added a classic "render loop":
requestAnimationFrame(render);
function render(){
requestAnimationFrame(render);
sphere.rotation.y += 0.005; //rotation stuff, just for fun
renderer.render(scene, camera);
};
requestAnimationFrame function works like a timer, calling to the render function each time the browser is ready to update the web page.
BTW, Three.js works fine with Firefox.