I have 20 objects and I'm creating mesh and textures with three.js. You can see the example object.
What I'm trying to do is exactly, I display my first object, then let it stay on the screen for a while, then turn it off and display my new object. In this way, I want to show my 20 objects in order. The waiting time is important to me, I want this time to be around 0.5. How can I do it.
Adding all meshes a array:
for ( var i = 0; i < count; i ++ ) {
geometry = // new geometry ...
material = // new material ...
//the lines above are unnecessary
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
objects.push(mesh);
}
And this is the render part:
function render() {
requestAnimationFrame(render);
for ( var i = 0; i < count; i ++ ) {
var mesh = objects[ i ];
}
renderer.render(scene, camera);
}
As I said, 0.5 seconds after showing my first object, I have to turn it off and show my other object. But I couldn't find a solution. What should I do?
You just need something like this:
let index = Math.floor(t/500) % meshes.length;
Full example:
const renderer = new THREE.WebGLRenderer({antialias:true});
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 10);
camera.position.setScalar(1.3);
camera.lookAt(scene.position);
const material = new THREE.MeshNormalMaterial();
const meshes = [
new THREE.BoxGeometry(),
new THREE.SphereGeometry(),
new THREE.CylinderGeometry(),
new THREE.ConeGeometry(),
new THREE.IcosahedronGeometry(),
new THREE.OctahedronGeometry(),
new THREE.TorusGeometry(),
new THREE.TorusKnotGeometry(),
new THREE.DodecahedronGeometry(),
].map(geometry => new THREE.Mesh( geometry, material ));
let currentIndex = null;
requestAnimationFrame(function render(t) {
if (renderer.width !== innerWidth || renderer.height !== innerHeight){
renderer.setSize(innerWidth, innerHeight);
camera.aspect = innerWidth/innerHeight;
camera.updateProjectionMatrix();
}
const index = Math.floor(t/500) % meshes.length;
if (currentIndex !== index) {
scene.remove(meshes[currentIndex]);
scene.add(meshes[index])
currentIndex = index;
}
scene.rotation.y += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(render);
});
body { margin: 0; overflow:hidden}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/107/three.min.js"></script>
Related
In the last two months i have learned a lot about three.js and javascript. i have now gone deeper into the handling of shaders and i once saw a nice example of creating procedural planets on the web. I find spherical geometries interesting because their non-euclidean geometry makes them a nice challenge for textures.
I have shown the main code here. my problem is that the texture rendering doesn't work.
I have tested the individual components of my program. The shader and the planet class, all functions work correctly.
the problem is the renderer. I need this to display the scene but also to create the textures. When in the function
textureGeneratorMaterial (below) renderer.render(textureScene, textureCamera, texture, true); is activate, I only get a black screen. There after the function renderer.render(scene, camera); is called, I mean that the normal scene should be seen. Does the program remain trapped in the texture scene? Why does renderer.render(scene, camera) not work, if the renderer is also called in textureGeneratorMaterial?
I've already tried to create a second renderer, but that doesn't work either. At the moment i don't know what to do, because i don't understand the cause. Someone knows why the renderer doesn't want the way I want?
var camera, controls, scene, renderer, container;
var sunLight, ambientlight;
var test1, test2, test3, test4;
function main() {
init();
animate();
}
function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container = document.getElementById('container');
renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild( renderer.domElement );
var aspect = container.clientWidth / container.clientHeight;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x000000 );
camera = new THREE.PerspectiveCamera( 45, container.clientWidth / container.clientHeight, 1, 10000 );
camera.position.set(0, 0, 20);
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = true;
controls.enabled = true;
controls.target.set(0, 0, 0);
//-------------
sunLight = new THREE.PointLight(new THREE.Color(0xffffff), 1.0);
sunLight.position.set(100, 0, 0);
scene.add(sunLight);
ambientlight = new THREE.AmbientLight( 0xF0F0F0 ); // soft white light
scene.add( ambientlight );
var maps = generateTextures(); //--here is the problem---
/* -----used ro check "function textureGeneratorMaterial(index)" works------
var plane = new THREE.Mesh(
new THREE.PlaneGeometry(1024, 1024),
textureGeneratorMaterial(0)
);
scene.add( plane );
*/
/*-----used ro check "Planet class" works------
for(var i = 0; i <6;i++){
maps.textureMaps[i].texture = new THREE.TextureLoader().load("img/Cay_sand.jpeg");
maps.bumpMaps[i].texture = new THREE.TextureLoader().load("img/Cay_sand.jpeg");
}
*/
scene.add(new Planet(5, maps.textureMaps, maps.bumpMaps)); // works correct
}//-------End init----------
function animate() {
requestAnimationFrame( animate );
render();
}//-------End animate----------
function render() {
document.getElementById("demo1").innerHTML = test1;
document.getElementById("demo2").innerHTML = test2;
document.getElementById("demo3").innerHTML = test3;
document.getElementById("demo4").innerHTML = test4;
camera.updateMatrixWorld();
camera.updateProjectionMatrix();
renderer.render(scene, camera);
}//-------End render----------
function generateTextures() {
var textureMaps = [];
var bumpMaps = [];
var resolution = 1024;
for (var index = 0; index < 6; index++) {
var texture = new THREE.WebGLRenderTarget(resolution, resolution, {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat});
var textureCamera = new THREE.OrthographicCamera(-resolution/2, resolution/2, resolution/2, -resolution/2, -100, 100);
textureCamera.position.z = 10;
var textureScene = new THREE.Scene();
var plane = new THREE.Mesh(
new THREE.PlaneGeometry(resolution, resolution),
textureGeneratorMaterial(index)
);
plane.position.z = -10;
textureScene.add(plane);
// renderer.render(textureScene, textureCamera, texture, true);
var buffer = new Uint8Array(resolution * resolution * 4);
var gl = renderer.getContext();
gl.readPixels( 0, 0, resolution, resolution, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
textureMaps.push(texture);
bumpMaps.push({image: {data: buffer, height: resolution, width: resolution}});
}
return {textureMaps: textureMaps, bumpMaps: bumpMaps};
}
Ok i have found a solution. I was convinced that it was up to the renderer and that it would remain trapped in the texture target when it was called up for the first time.
In three.js there is the possibility to reset the render target and if you do that, three.js seems to see it as in need of initialization and accepts the call to the renderer to render the main scene.
I added this line: renderer.setRenderTarget(null);
See below
function generateTextures() {
var textureMaps = [];
var bumpMaps = [];
var resolution = 2048;
for (var index = 0; index < 6; index++) {
var texture = new THREE.WebGLRenderTarget(resolution, resolution, {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat});
var textureCamera = new THREE.OrthographicCamera(-resolution/2, resolution/2, resolution/2, -resolution/2, -100, 100);
textureCamera.position.z = 10;
var textureScene = new THREE.Scene();
var plane = new THREE.Mesh(
new THREE.PlaneGeometry(resolution, resolution),
textureGeneratorMaterial(index)
);
plane.position.z = -10;
textureScene.add(plane);
renderer.render(textureScene, textureCamera, texture, true);
var buffer = new Uint8Array(resolution * resolution * 4);
var gl = renderer.getContext();
gl.readPixels( 0, 0, resolution, resolution, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
textureMaps.push(texture);
bumpMaps.push({image: {data: buffer, height: resolution, width: resolution}});
renderer.setRenderTarget(null);
}
return {textureMaps: textureMaps, bumpMaps: bumpMaps};
}
In older versions like the example I saw, this wasn't necessary. But this is not meant as a criticism. It's a clean solution. So three always knows that there is a need for renderer action.
So I am having this code:
computeCarBoundingBox(mesh);
mesh.rotation.x = this.rotationVal[ 0 ];
mesh.rotation.y = this.rotationVal[ 1 ];
mesh.rotation.z = this.rotationVal[ 2 ];
Where I try to compute a bounding box for a mesh, if I compute it after rotation look like this:
If I compute it after the rotation look like this:
My compute bounding box function is this:
function computeCarBoundingBox(mesh){
var box = new THREE.Box3().setFromObject(mesh);
var boundingBoxHelper = new THREE.Box3Helper( box, 0xffff00 );
scope.carBoundingBox =boundingBoxHelper;
scene.add(scope.carBoundingBox);
console.log(box.min); // x, y, and z are all Infinity.
console.log(box.max); // x, y, and z are all -Infinity.
}
I do have a geometry. This is a part of my code :
this.loadCar = function ( carsVector,carName,roadName ) {
if(carName=='veyron')
{
var index = 0;
}
else if(carName=='F50')
{
var index = 1;
}
else
{
var index = 2;
}
console.log("Selected car name:"+carName);
var carLoader = new THREE.BinaryLoader();
carLoader.load( carsVector[Object.keys(carsVector)[index]].url, function( geometry ) {
geometry.sortFacesByMaterialIndex();
console.log("url--->"+carsVector[Object.keys(carsVector)[index]].url);
var materials = [];
this.scaleVal = carsVector[ Object.keys(carsVector)[index] ].scale * 1;
if(roadName =='road01'){
this.positionVal = carsVector[ Object.keys(carsVector)[index] ].position_r1;
}
else if(roadName=='road02'){
this.positionVal = carsVector[ Object.keys(carsVector)[index] ].position_r2;
}
this.rotationVal = carsVector[ Object.keys(carsVector)[index] ].init_rotation;
for ( var i in carsVector[ Object.keys(carsVector)[index] ].materialsMap ) {
materials[ i ] = carsVector[ Object.keys(carsVector)[index] ].materialsMap[ i ];
}
createObject(geometry,materials);
});
return scope.carMesh;
}
// internal helper methods
function createObject ( geometry, materials ) {
scope.carGeometry = geometry;
scope.carMaterials = materials;
createCar();
};
function createCar () {
console.log("CREATE CARRRRRRRRRRRRRRRRR");
if ( scope.carGeometry ) {
var carMaterial = new THREE.MeshFaceMaterial( scope.carMaterials );
var mesh = new THREE.Mesh( scope.carGeometry, carMaterial );
mesh.scale.x = mesh.scale.y = mesh.scale.z = this.scaleVal;
mesh.position.set( this.positionVal[0], this.positionVal[1], this.positionVal[2]);
mesh.rotation.x = this.rotationVal[ 0 ];
mesh.rotation.y = this.rotationVal[ 1 ];
mesh.rotation.z = this.rotationVal[ 2 ];
this.carMesh = mesh;
//
computeCarBoundingBox(mesh);
console.log("This car mesh"+this.carMesh);
addShadows();
scene.add(this.carMesh);
//this.carBoundingBox.rotation.x =this.r[0];
//this.carBoundingBox.rotation.y = this.r[1];
//this.carBoundingBox.rotation.z = this.r[2];
//scene.add( this.carBoundingBox );
}
if ( scope.callback ) {
scope.callback(this.carMesh);
}
}
These are the methods I'm using in my project where I add the bounding boxes after rotation. If you don't rotate first you don't need the adjustRelativeTo step see e.g. https://codepen.io/seppl2019/pen/zgJVKM
class ChildPart {
constructor(mesh) {
this.mesh=mesh;
this.boxwire=null;
}
// add my bounding box wire to the given mesh
addBoundingBoxWire(toMesh) {
var boxwire = new THREE.BoxHelper(this.mesh, 0xff8000);
this.boxwire=boxwire;
ChildPart.adjustRelativeTo(boxwire,toMesh);
toMesh.add(boxwire);
}
static adjustRelativeTo(mesh,toMesh) {
//logSelected("adjusting toMesh",toMesh);
//logSelected("beforeAdjust",this.mesh);
toMesh.updateMatrixWorld(); // important !
mesh.applyMatrix(new THREE.Matrix4().getInverse(toMesh.matrixWorld));
//logSelected("afterAdjust",this.mesh);
}
}
I run into this problem recently. Thanks to #Wolfgang Fahl 's resolution.
Tt’s the right direction, but when I was doing it, I found something was wrong.
When the mesh have rotation effection. the box is still bigger than original one.
So you need to remove rotation before create BoxHelper, then add rotation back.
static adjustRelativeTo(mesh, toMesh) {
toMesh.updateMatrixWorld(); // important !
mesh.applyMatrix4(new THREE.Matrix4().copy( toMesh.matrixWorld ).invert());
}
addBoundingBox(mesh, toMesh) {
// remove rotation
let rotate = mesh.rotation.clone();
mesh.rotation.set(0, 0 , 0);
let box = new THREE.BoxHelper( mesh, 0xffff00);
// apply to parent matrix
adjustRelativeTo(box, toMesh);
toMesh.add(box);
// 然后再把旋转加上
mesh.rotation.set(rotate.x, rotate.y, rotate.z);
}
That's how .setFromObject() works, when object is wide and when you rotate it, its box will be bigger, as it's world-axis-aligned:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 5, 5);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xffffff, 0.75);
light.position.set(-10, 10, -10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.25));
scene.add(new THREE.GridHelper(10, 10));
var geometry = new THREE.BoxGeometry(2, 1, 3);
geometry.translate(0, 0.5, 0);
var mesh1 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: "gray"
}));
mesh1.position.x = -2.5;
scene.add(mesh1);
var mesh2 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: "aqua"
}));
mesh2.position.x = 2.5;
mesh2.rotation.y = THREE.Math.degToRad(45);
scene.add(mesh2);
var bbox1 = new THREE.Box3().setFromObject(mesh1);
var bbox2 = new THREE.Box3().setFromObject(mesh2);
var bhelp1 = new THREE.Box3Helper(bbox1, 0xffff00);
scene.add(bhelp1);
var bhelp2 = new THREE.Box3Helper(bbox2, 0xff00ff);
scene.add(bhelp2);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Alright I have two theories, and no certain answer. sorry!
1) It is possible for meshes to be without geometry. Does your mesh have a geometry? If not the code called from setFromObject will fail. (expandByPoint will never be called and min and max will remain at Infinity since the previous makeEmpty-call).
2) Seeing how deeply dependent that recursive "expandByOject" code is on scope and this, I would try adding parenthesis to your new-operator var box = (new THREE.Box3()).setFromObject(mesh); It's a bit of a shot in the dark, but perhaps the scope is never properly set.
Sorry for not taking the time and testing things out first.
An example of what I'm trying to achieve: https://workshop.chromeexperiments.com/examples/guiVR/#1--Basic-Usage
How could I get the Google Cardboard crosshair, (gaze)pointer, reticle, whatever you want to call it effect in three.js? I would like to make a dot, as crosshair, in the center of the screen in my scene. Like that I would like to use a raycaster to identify what I'm looking at in VR. Which way would be best to go here?
Do I fake an X and Y position of my mouse? Because I found other people have answered how to cover this by adding an event listener to the mousemove event. But this works on desktop, and I want to bring this to mobile.
Here are the main parts needed for building your own gaze cursor:
An object that serves as the indicator (cursor) for user feedback
An array of objects that you want the cursor to interact with
A loop to iterate over the array of interactive objects to test if the cursor is pointing at them
Here's an example of how this can be implemented.
Gaze cursor indicator
Using a ring here so it can be animated, as you typically want to give the user some feedback that the cursor is about to select something, instead of instantly triggering the interaction.
const cursor = new THREE.Mesh(
new THREE.RingBufferGeometry(0.1, 0.15),
new THREE.MeshBasicMaterial({ color: "white" })
);
Interactive Objects
Keep track of the objects you want to make interactive, and the actions they should execute when they are looked at.
const selectable = [];
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(1, 1, 1),
new THREE.MeshNormalMaterial()
);
selectable.push({
object: cube,
action() {
console.log("Cube selected");
},
});
Checking Interactive Objects
Check for interactions on every frame and execute the action.
const raycaster = new THREE.Raycaster();
(function animate() {
for (let i = 0, length = selectable.length; i < length; i++) {
const camPosition = camera.position.clone();
const objectPosition = selectable[i].object.position.clone();
raycaster.set(camPosition, camera.getWorldDirection(objectPosition));
const intersects = raycaster.intersectObject(selectable[i].object);
const selected = intersects.length > 0;
// Visual feedback to inform the user they have selected an object
cursor.material.color.set(selected ? "crimson" : "white");
// Execute object action once
if (selected && !selectable[i].selected) {
selectable[i].action();
}
selectable[i].selected = selected;
}
})();
Here's a demo of this in action:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.121.1/build/three.module.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three#0.121.1/examples/jsm/controls/OrbitControls.js";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const cameraMin = 0.0001;
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.PerspectiveCamera(75, aspect, cameraMin, 1000);
const controls = new OrbitControls(camera, renderer.domElement);
const scene = new THREE.Scene();
camera.position.z = 5;
scene.add(camera);
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(),
new THREE.MeshNormalMaterial()
);
cube.position.x = 1;
cube.position.y = 0.5;
scene.add(cube);
const cursorSize = 1;
const cursorThickness = 1.5;
const cursorGeometry = new THREE.RingBufferGeometry(
cursorSize * cameraMin,
cursorSize * cameraMin * cursorThickness,
32,
0,
Math.PI * 0.5,
Math.PI * 2
);
const cursorMaterial = new THREE.MeshBasicMaterial({ color: "white" });
const cursor = new THREE.Mesh(cursorGeometry, cursorMaterial);
cursor.position.z = -cameraMin * 50;
camera.add(cursor);
const selectable = [
{
selected: false,
object: cube,
action() {
console.log("Cube selected");
},
}
];
const raycaster = new THREE.Raycaster();
let firstRun = true;
(function animate() {
requestAnimationFrame(animate);
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
controls.update();
if (!firstRun) {
for (let i = 0, length = selectable.length; i < length; i++) {
const camPosition = camera.position.clone();
const objectPosition = selectable[i].object.position.clone();
raycaster.set(camPosition, camera.getWorldDirection(objectPosition));
const intersects = raycaster.intersectObject(selectable[i].object);
const selected = intersects.length > 0;
cursor.material.color.set(selected ? "crimson" : "white");
if (selected && !selectable[i].selected) {
selectable[i].action();
}
selectable[i].selected = selected;
}
}
renderer.render(scene, camera);
firstRun = false;
})();
</script>
This library works like a charm - https://github.com/skezo/Reticulum
I'm using the Three.js javascript library. To test it I downloaded the an example from here.
I'm trying to display several times the same element using a for loop. There two questions related (1, 2) but it's not exactly what I want. My problem is that if I create the element inside the loop it will only display the last element of the iteration. In this particular case the element in position (12,12).
But, if I do an action like an alert it will display all the elements. Also if I have any other functions that delays the execution.
I saw some examples running, as the mrdoob examples, but I would like this code running because I need to load several mesh instead of generating primitive figures.
// Set up the scene, camera, and renderer as global variables.
var scene, camera, renderer;
var group;
// Call functions
init();
animate();
// Sets up the scene.
function init() {
// Iterator
var i, j;
// Create the scene and set the scene size.
scene = new THREE.Scene();
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
// Create a renderer and add it to the DOM.
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(WIDTH, HEIGHT);
document.body.appendChild(renderer.domElement);
// Create a camera, zoom it out from the model a bit, and add it to the scene.
camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 0.1, 20000);
camera.position.set(0,20,20);
scene.add(camera);
// Create an event listener that resizes the renderer with the browser window.
window.addEventListener('resize', function() {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
// Set the background color of the scene.
renderer.setClearColor(0x333F47, 1);
// Create a light, set its position, and add it to the scene.
var light = new THREE.PointLight(0xffffff);
light.position.set(-100,200,100);
scene.add(light);
group = new THREE.Object3D();
for(i=0; i < 15; i+=3) {
for(j=0; j < 15; j+=3) {
var loader = new THREE.JSONLoader();
loader.load( "models/treehouse_logo.js", function(geometry){
var material = new THREE.MeshLambertMaterial({color: 0x55B663});
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(i,0,j);
group.add(mesh);
});
//alert("iteration"+i+" "+j);
}
}
scene.add( group );
// Add OrbitControls so that we can pan around with the mouse.
controls = new THREE.OrbitControls(camera, renderer.domElement);
}
// Renders the scene and updates the render as needed.
function animate() {
// Read more about requestAnimationFrame at http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
requestAnimationFrame(animate);
// Render the scene.
renderer.render(scene, camera);
controls.update();
}
What you are doing here is incredibly inefficient:
for(i=0; i < 15; i+=3) {
for(j=0; j < 15; j+=3) {
var loader = new THREE.JSONLoader();
loader.load( "models/treehouse_logo.js", function(geometry){
var material = new THREE.MeshLambertMaterial({color: 0x55B663});
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(i,0,j);
group.add(mesh);
});
//alert("iteration"+i+" "+j);
}
}
This would be much better done like this (untested):
var loader = new THREE.JSONLoader();
loader.load( "models/treehouse_logo.js", function( geometry ){
var material, mesh, i, j, instance;
material = new THREE.MeshLambertMaterial({ color: 0x55B663 });
mesh = new THREE.Mesh( geometry, material );
for ( i = 0; i < 15; i += 3 ) {
for ( j = 0; j < 15; j += 3 ) {
instance = mesh.clone();
instance.position.set( i, 0, j );
group.add( instance );
}
}
});
You'd need to do repeat this pattern for each unique mesh.
The problems your current approach has are:
More memory needed by the GPU for each identical mesh
More memory needed by the browser to remember each identical mesh
More processing power required by the GPU as more memory needs to be processed
Each time you call the loader, you instruct the browser to execute a request. That's some 25 identical requests in your case. They should come from the cache, but it'll still be slow.
You may have variables scoping issues too which gives issues with the loader callback, but I'm not entirely sure about that.
alert() makes for a very poor debugging tool by the way as it changes the way the browser reacts: it stops executing JavaScript when the alert is open and that affects the loader and similar things. You're better off with the Console logging methods.
I would say it is because you are setting the loader variable in each iteration of the loop which will override the loader of the last iteration.
Why is the actual loading being done in a loop? Why not load it once and clone it?
eg.
group = new THREE.Object3D();
var loader = new THREE.JSONLoader();
loader.load( "models/treehouse_logo.js", function(geometry){
var material = new THREE.MeshLambertMaterial({color: 0x55B663});
for(i=0; i < 15; i+=3) {
for(j=0; j < 15; j+=3) {
var mesh = new THREE.Mesh(geometry.clone(), material);
mesh.position.set(i,0,j);
group.add(mesh);
}
}
});
scene.add( group );
The above code is untested
I used the OBJMTLLoader class for one obj file and rotation worked well around a fixed point on the object by using object.rotation.y -= 0.5. Using the same code (minus changing the camera position), I replaced the .obj file with another and the rotation is now going in a circular motion, like around the camera instead of staying in place. Any idea why when I used the same code?
Thanks
EDIT:
var OBJLoaded;
function init()
{
container = document.getElementById('player');
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 2000);
camera.position.x = 110;
camera.position.z = -160;
camera.position.y = 15;
// camera.position.z = 40;
// camera.position.y = 2;
//scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 100, 90, 200 );
scene.add( directionalLight );
//model
var loader = new THREE.OBJMTLLoader();
//loader.load('./assets/Stereo_LowPoly.obj', './assets/Stereo_LowPoly.mtl', function(object)
loader.load('./assets/studio_beats.obj', './assets/studio_beats.mtl', function(object)
{
OBJLoaded = object;
console.log(object);
scene.add( object );
});
renderer = new THREE.WebGLRenderer({alpha: true});
renderer.setClearColor(0x000000, 0);
renderer.setSize($('#player').width(), $('#player').height());
container.appendChild(renderer.domElement);
scene.add(camera);
}
function animateBoombox()
{
requestAnimationFrame(animateBoombox);
render();
}
function render()
{
var rotSpeed = 0.004;
if (OBJLoaded)
{
OBJLoaded.rotation.y -= rotSpeed;
}
renderer.render(scene, camera);
}
The parts commented (camera and object load) is for the previous object that was loaded. That works fine, but the uncommented partdoes not work the same.
The object you loaded has a pivot point which came from the model creater software... You need to change the pivot point of the object before you load it with three.js.
If you cannot, you should do it like i had in loader callback:
var loader = new THREE.OBJMTLLoader();
loader.load('your_file.obj', 'your_file.mtl', function (object) {
object.traverse(function (child) {
child.centroid = new THREE.Vector3();
for (var i = 0, l = child.geometry.vertices.length; i < l; i++) {
child.centroid.add(child.geometry.vertices[i].clone());
}
child.centroid.divideScalar(child.geometry.vertices.length);
var offset = child.centroid.clone();
child.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(-offset.x, -offset.y, -offset.z));
child.position.copy(child.centroid);
child.geometry.computeBoundingBox();
});
});
Then rotate your object...