I'm trying to create several duplicates of a mesh randomly distributed around my THREE.Js scene. I've used the clone() method in a for loop to do so. The array shows a list of the clones I created in the console, but when I try to call the individual elements of the array, I get undefined returned to me in the console. This is the code I tried:
let ball = new THREE.Mesh();
loader.load( './ball.gltf', function ( gltf ) {
gltf.scene.traverse(function(model) { //for gltf shadows!
if (model.isMesh) {
model.castShadow = true;
model.material = sphereMaterial;
}
});
ball = gltf.scene
scene.add( ball );
for (let i = 1; i <= 9; i++) {
let newBall = ball.clone()
scene.add(newBall)
newBall.position.set(Math.floor(Math.random() * 20) * 2 - 10, Math.floor(Math.random() * 20) * 3 - 10, Math.floor(Math.random() * 20) * 2 - 10)
pos_arr.push(newBall)
}
}, undefined, function ( error ) {
console.error( error );
} );
This is what I get when I call console.log(pos_arr) :
Then, if I were to call, say console.log(pos_arr[2]), I would get undefined.
By any chance, does anyone know what's going wrong here?
The usual root cause of this issue is that you access pos_arr too early. Meaning before the onLoad() callback of GLTFLoader has been executed.
There are various ways to fix this issue and the solution mainly depends on how you have structured your app. One approach that can always be used is to pass an instance of LoadingManager to all of your loaders and use its onLoad() callback to finish your scene setup or start object access.
Related
for a three.js project I have I have run into a few problems loading vertices from an STL and adequately converting them to world coordinates. It seems the matrix isn't being applied properly and, I think, it might be related to the loading mechanism itself.
loader.load( './assets/models/trajan_print.stl', function ( geometry ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.name = "target";
mesh.position.set( 0, - 300, - 400 );
mesh.rotation.set( - Math.PI / 2, 0, Math.PI );
mesh.scale.set( 5, 5, 5 );
//mesh.castShadow = true;
//mesh.receiveShadow = true;
mesh.visible = false;
SCENE.add( mesh );
model.setTargets(mesh);
} );
the important function to note is the last one. model.setTargets(mesh). I'm interested in the vertices of the object in world coordinates and that's what that function does... kinda of:
setTargets(mesh){
this.matrixWorld = mesh.matrixWorld; //THIS WORKS, PRINTING IT REVEALS VALUES TRANSLATION/SCALE/ROTATION THAT MATCH THE MODEL'S
var buffer = mesh.geometry.attributes.position.array;
for(var i = 0; i < buffer.length /3; i = i + 3){
var point = new THREE.Vector3(buffer[i], buffer[i+1],buffer[i+2]);
point.applyMatrix4(this.matrixWorld);//DOES NOT WORK
this.unassignedVertices.push(point);
}
}
Now if I do the exact same operation outside of this function it will work as intended. This one is only called if this.unassignedVertices so it was my way around the fact that I needed to wait for the asynchronous load to happen.
insertParticle(part) {
var point = this.unassignedVertices.pop();
point.applyMatrix4(this.matrixWorld); //THIS WORKS BUT HERE BUT WHY?
part.setTargetPoint(point);
this.octree.add(part);
this.particles.add(part);
}
Problem number two, relates back to setTargets(mesh) I seem to only be loading around only half of the vertices from mesh.geometry.attributes.position.array. Now this can actually be caused by other parts in the code and I think that is something that falls outside the scope of a SO question so my question is if anything on that function could be responsible for it? Am I loading it improperly, am I converting it wrong, am I skipping points?
As for further context : the model loads and displays just fine if I remove the visible = false tag.
Ok so if anyone runs into this problem. The array will have duplicate positions as not all of them refer to vertices (probably). Simple case of if(position.x == ... cleans it right up to what's expected.
I am trying to make a series of cubes that can be clicked to highlight them. This will enable me to change their color or add a texture or manipulate them in some way. I have looked through the source code of all the interactive examples at https://threejs.org/examples/ and it appears that each example uses a slightly different way of creating and selecting objects in the scene. I am not used to using javascript though, so maybe I'm missing something simple.
I create an Object3D class named blocks to store all of the cubes
blocks = new THREE.Object3D()
I am using a for loop to create a 9 x 9 array of cubes starting at (0,0,0) coordinates with a slight gap between them, and add() them to blocks and add() blocks to the scene. example: (cube size 2,2,2)
function stack(mx,my,mz){
for (var i = 0; i < 9; i++){
line(mx,my,mz);
mz += 3;
}
}
function line(mx,my,mz){
for (var i = 0;i<9;i++){
var block = new THREE.Mesh( Geometry, Material);
block.position.x = mx;
block.position.y = my;
block.position.z = mz;
blocks.add(block);
mx+=3;
}
}
stack(mx,my,mz)
scene.add(blocks)
When I run this code, I can see them rendered. I use raycaster to .intersectObjects() which requires an array of objects. This is where I run into the problem of selecting just one object.
function onDocumentMouseDown(event) {
var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
**var intersects = raycaster.intersectObjects(blocks.children, true);**
if (intersects.length > 0) {
intersects[0].object.material.transparent = true;
other code stuff blah blah blah
{
This will make all children clickable but they have the same .id as the first object created. so if I try to .getObjectById() in order to change something, it doesn't work.
I have tried to generate each element and add them to the scene iteratively instead of creating an object array to hold them and it still has a similar effect. I've tried storing them in a regular array and then using true parameter to recursively search the .intersectObject() array but it selects all of the objects when I click on it.
var intersects = raycaster.intersectObjects(blocks, true);
I have considered creating 81 unique variables to hold each element and statically typing an array of 81 variables (desperate option) but I can't find a secure way to dynamically create variable names in the for loop to hold the objects. This way was posted on stackoverflow as a solution to creating different named variables but it doesn't seem to create variables at all.
for (var i=0, i<9, i++){
var window["cube" + i] = new THREE.Mesh( Geometry, Material)
{
Main Question: How can I iteratively create multiple Mesh's (enough that statically typing each variable would be ill-advised) in a controllable way that I can select them and manipulate them individually and not as a group?
I think the reason why you met this problem is you reference same Material to build your Mesh, you did intersect a single object in blocks.children, but when you change some properties of the material others mesh who use the material would change too.
function line(mx,my,mz){
for (var i = 0;i<9;i++){
material = new THREE.MeshLambertMaterial({color: 0xffffff});
var block = new THREE.Mesh( Geometry, material);
block.position.x = mx;
block.position.y = my;
block.position.z = mz;
blocks.add(block);
mx+=3;
}
}
it works for me.
I exported a .json from the online 3D editor and I'm trying to load it and instantiate 20 versions of it, like this example. My code is flawed bc all 20 versions are actually acting like the same object. Not sure why they're not being added to the scene as separate objects in their given x,z coordinates.
var serverObject;
var allBrains = []
var xCenter;
var zCenter;
var spacing = .2;
var newServ;
var objectLoader = new THREE.ObjectLoader();
objectLoader.load("asset_src/model.json", function(obj) {
//give it a global name, so I can access it later?
serverObject = obj
//see what's inside of it
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
console.log(child)
}
})
//was trying to add transparency this way, but ended up
//going through the online editor to apply it
// var cubeMaterial1 = new THREE.MeshBasicMaterial({
// color: 0xb7b7b7,
// refractionRatio: 0.98
// });
//Do i need to instantiate my mesh like this, if so, how do I make sure that it gets the materials from the json? The json has 6 children each with a different material
// serverObject = new THREE.Mesh( obj, cubeMaterial1 );
//make 20 versions of the file
for (var i = 0; i < 20; i++) {
xCenter = Math.cos(toRadians(i * spacing))
zCenter = Math.sin(toRadians(i * spacing))
serverObject.scale.set(.09, .09, .09)
//this amount of offset is correct for the scale of my world
//i was going to use my xCenter, zCenter but tried to simplify it till it works
serverObject.position.set((i * .1), controls.userHeight - 1, i * .1);
allBrains.push(serverObject)
//I've attempted a number of ways of adding the obj to the scene, this was just one
scene.add(allBrains[i]);
}
// see that there are 20 meshes
console.log(allBrains)
});
The return of my last console log looks like this:
At the moment, you have a single object (serverObject) which you manipulate and add multiple times, but each iteration of the loop just modifies the same object, overriding previous parameters.
You need to clone your mesh, using the... clone() method. You'll then be able to modify the settings of that object (the copy), and each of the meshes will remain independent.
Alternatively, you could run the objectLoader.load method inside the loop to create the object multiple times from the JSON file, but it's probably a waste of resources.
Thanks to #jcaor for the clone() idea, here is the working code:
var objectLoader = new THREE.ObjectLoader();
objectLoader.load("asset_src/model.json", function(obj) {
//give it a global name, so I can access it later
serverObject = obj
//see what's inside of it
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
console.log(child)
}
})
for (var i = 0; i < 20; i++) {
var tempNew = serverObject.clone()
xCenter = Math.cos(toRadians(i * spacing))
zCenter = Math.sin(toRadians(i * spacing))
tempNew.scale.set(.05, .05, .05)
tempNew.position.set(xCenter, controls.userHeight - 1, zCenter);
allBrains.push(tempNew)
scene.add(allBrains[i]);
}
console.log(allBrains)
});
This looks like a pointer issue to me.
In JavaScript you can think of variables like pointers that is why you don't have to assign types to variables and how functions can be variables and still work in normal computer science.
In this case you are assigning the same pointer to each slot in the array.
This is a super simple version of the problem. obj2.foo was never changed but because we changed obj.foo obj2.foo changed because of the var simply pointing to the same object.
var obj = {
foo : 1
}
var obj2 = obj;
obj.foo = 2;
console.log(obj2.foo);
What I would do is create a new object and popluate it with the information of the master object in you case "serverObject"
allBrains.push(serverObject)
firstly
I am not beginner at game development at all - but beginner in web game development specially
I started with Phaser as it looks good and optimized for mobile games
anyway ..
I have a strange behavior with my game - I put a rectangle and trying to move it (when I debugged the X axis already changes correctly , but I can't see the rectangle move!!)
my codes
var game = new Phaser.Game(window.innerWidth,window.innerHeight,Phaser.AUTO);
var colors = ["#FF0000" , "#00FF00" , "#0000FF" , "#FFFF00" , "#00FFFF" , "#FFFF00"];
var lst;
var hlprs = [];
var gameState = {
preload: function () {
this.game.stage.backgroundColor = "#FFFFFF";
},
create: function () {
for (var i = 0 ; i < 8 ; i++)
{
hlprs[i] = new Phaser.Rectangle((i*200),0,100,20);
hlprs[2*i + 1] = new Phaser.Rectangle((i*200),window.innerHeight - 20,100,20);
game.debug.geom(hlprs[i] , colors[Math.floor((Math.random() * 6))]);
game.debug.geom(hlprs[2*i + 1] , colors[Math.floor((Math.random() * 6))]);
}
},
update: function ()
{
moving();// it calls moving function and X axis is changes but (the rectangle does not move !!!)
}
};
function moving()
{
for (var i = 0 ; i < 8 ; i++)
{
hlprs[i].offset(-1,0);
hlprs[2*i + 1].offset(-1,0);
}
}
game.state.add('GameState' , gameState);
game.state.start('GameState');
Without testing it, I'd guess that what happens is the following: you create a bunch of shapes and do a single call to game.debug.geom() for each of the shapes. In the meantime the shapes do start moving, but since you never call game.debug.geom() again, you never see anything moving.
If you intend to use methods from game.debug, they should usually go inside the render() method of your state (which will be called once for every frame).
Note that the debug methods are to be used, well, for debugging. The proper way of displaying a shape is by making a sprite or an image (in which case you won't have to update anything manually since Phaser will handle it).
Since the update() function calls moving() you might want to have your game.debug commands within moving
function moving()
{
for (var i = 0 ; i < 8 ; i++)
{
hlprs[i].offset(-1,0);
hlprs[2*i + 1].offset(-1,0);
// update here
game.debug.geom(hlprs[i] , colors[Math.floor((Math.random() * 6))]);
game.debug.geom(hlprs[2*i + 1] , colors[Math.floor((Math.random() * 6))]);
}
}
Here's a demo: https://jsfiddle.net/user2314737/J5fUE/253/
Using THREE.js I would like to animate the size of an object so it slowly shrinks into nothingness.
Using answers from Three.js - Animate object size I can change the size of an object, but this size change is instant and I want the change to happen over a period of time (3 seconds). Plus the question is old :)
This is my current code. backwardMeshOct is simply a THREE.Mesh(geometry, material):
var time = 20;
function animate() {
backwardMeshOct.scale.x = Math.abs( Math.sin( time * 50 ) );
}
requestAnimationFrame(animate);
I've tried altering var time and what time is multiplied by but the result is still the same, an instant size scale on x.
Thanks for your help doods and doobs.
You might find it easier to use a library like Tween.JS. You'll be able to define a starting value, a target value, and a time delta for the change!
Check out the answer to this question for how it can work with Three: Tween JS basics on three JS cube
If you aren't able to use Tween, try a THREE.Clock object to keep track of the time and scale your mesh appropriately. I made one in a global scope (before init) and then used it to count to 3 seconds:
function render() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
var t = clock.getElapsedTime();
if (t >= 3.0)
{
clock = new THREE.Clock;
mesh.scale.set(1,1,1);
}
else
{
mesh.scale.x = 1-(t/3.0);
mesh.scale.y = 1-(t/3.0);
mesh.scale.z = 1-(t/3.0);
}
renderer.render(scene, camera);
}
full example here: http://jsfiddle.net/adamfinley/ac6zxgt6/1/