I added some objects to ThreeJS scene (using scene.add(object)). I know the name of object and just want to fetch index of it from scene.children.
I tried to use scene.children.indexOf("objectName") but it returns -1 index. Can anybody suggest what can I do?
Thanks
var object = scene.getObjectByName( "objectName" );
or to recursively search the scene graph
var object = scene.getObjectByName( "objectName", true );
Alternatively, you can search by ID.
var id = scene.getObjectById( 4, true );
three.js r.60
I believe that something that can actually can answer your question is the following code that I use to clean the scene:
while (scene.children.length > 0) {
scene.remove(scene.children[scene.children.length - 1]);
}
this way I don't need to access an element by its name or id. The object are simply fetched from the array with an index, so it's like scene.children[i].
You got an alert text as undefined because you're getting back an object not a value/text. You would need to alert something like scene.getObjectByName( "objectName" ).name or scene.getObjectByName( "objectName" ).id i believe.
The relevant docs: getObjectById, getObjectByName. An important note from getObjectByName:
Note that for most objects the name is an empty string by default. You will have to set it manually to make use of this method.
This could be why you were getting undefined. Also, if you are getting undefined, trying to access .name or .id is going to throw an error. If you were getting an object back you would see something like "[object Object]" in the alert.
Another important note I learned the hard way is that the value you need to pass to getObjectById is object.id, and not object.uuid. Also, object.id is essentially just the index of the object in the children array.
Putting it all together:
// var object = new THREE.Mesh(...) or similar
object.name = 'objectName';
scene.add(object);
var retrievedObject = scene.getObjectById(object.id);
// or
var retrievedObject = scene.getObjectByName('objectName');
alert(retrievedObject.name);
Related
I have a group of objects (actually 3D text on an arc) that I want to remove from the scene on a specific click. Does .remove not work on groups? Here's basically what I have:
$(".inscript").on("mousedown", function(event){
var x = scene.getObjectByName("inscriptArc");
scene.remove(x);
});
This answer seems to suggest you can (remove a group from a scene using .remove), but it isn't working for me.
THREE.Scene.getObjectByName returns the first instance of a child with that name. If you have multiples you won't catch them by calling it once.
To remove all instances I would use the THREE.Object.traverse(fn(child){}) function ie:
var children_to_remove = [];
scene.traverse(function(child){
if(child.name == "inscriptArc"){
children_to_remove.push(child);
}
});
children_to_remove.forEach(function(child){
scene.remove(child);
});
you might be able to just do
scene.traverse(function(child){
if(child.name == "inscriptArc"){
scene.remove(child);
}
});
but I think there are some cases where this could cause an error if you are loading/removing anything from the scene asyncronously - because removing a child might throw an error when that child's children get traversed. Not sure, so I would try the simple one and swap with the more complicated one if it causes issues.
Yes, you definitely can remove a group of objects. You might have named children of the object instead of the actual group. Function getObjectByName did not always work as promised in older three.js releases so maybe try this.
scene.children.forEach(child => child.name == "inscriptArc" ? scene.remove(child) : null)
I am using https://github.com/makehuman-js/makehuman-js
The example exports the mesh from the source. So I am trying to get it from the scene with where it is changed.
When I try to export my scene to an obj file it is empty:
var objscene = new THREE.OBJExporter().parse( self.scene );
var output = JSON.stringify( objscene, null, 2 );
saveAs (new Blob([output], {type : 'text/plain;charset=utf-8'} ), 'Avatar.obj');
I can count the objects in the scene. There are four.
var scene_size = app.scene.children.length;
var i = 0;
while(i < scene_size){
alert(app.scene.children[i])
i = i + 1;
}
However they have no names so I add a name to my main human object.
// HUMAN
this.human = new makehuman.Human(this.resources);
this.human.name = 'human';
So now I can retrieve the name of the object named human.
var scene_size = app.scene.children.length;
var i = 0;
while(i < scene_size){
var thisone = app.scene.children[i]
alert(thisone.name)
i = i + 1;
}
So, I can demonstrate the objects exist. I will assign names to the other objects later. What I cannot understand is why my export is empty. The file is 1kb in size and there is only "" in it when I open it in my editor.
Any insight would be appreciated. I've been pounding it for a week and I am at a loss... Thanks!
OBJExporter.parse() does not return a JSON object. So it makes no sense to use JSON.stringify() in this context. Have a look at the actual output in this example (you will see it's just a plain string).
In any event, I recommend to use GLTFExporter instead since glTF is the recommended format of three.js. You can use code snippets from the following example for your own project.
https://threejs.org/examples/#misc_exporter_gltf
I'm building a proof of concept test with CasperJS and I'm trying to wrap my head around when things are available and when they are not.
I'm a bit fuzzy on why evaluate() is required and why one cannot just get a page and go to town with document.querySelectorAll(), but that ends up giving me a nodeList with a length of zero... even after casper.echoing out the HTML and SEEING plenty of matching selectors.
Right now, I'm trying this:
var tile;
var pid;
casper.waitForSelector('.m-product-tile', function() {
tile = this.evaluate(function() {
return __utils__.findOne('.m-product-tile');
});
pid = this.evaluate(function()
return __utils__.findOne('.m-product-tile').getAttribute('data-pid');
});
//do cools stuff like click() on tile and make sure the URL has the pid in it
});
But this feels wrong as I am not sure __utils__.findOne() will always return the same element.
I'd love to be able to do this instead:
pid = tile.getAttribute('data-pid');
But when I attempt that, I get an error:
uncaughtError: TypeError: 'undefined' is not a function (evaluating 'tile.getAttribute('data-pid')')
Did tile somehow cease to be a DOMNode with a getAttributes method? Using typeof tile, it still thinks it's an object. I echoed out the DOMNode and it has a getAttributes key.
The code in the first example DOES work with getAttribute... so why does tile morph into something that no longer has a getAttributes method?
http://casperjs.readthedocs.org/en/latest/modules/casper.html#evaluate provides a pretty good answer... but still, I'd like to know why, if what the page is passing back, is a DOMNode, why can't I treat it like one?
I found this function to remove a gui element but i think it is outdated. So far I haven't been able to find anyone else who knows how to remove any part of a gui, whether its an entire dat.GUI() or just an added element to a dat.GUI(). The first would probably be enough for what i need (just removing the dat.GUI() all together) but either one would be super helpful!
supposed to remove a dat.GUI()
gui = new dat.GUI();
...
removeGui(gui);
function removeGui(gui, parent)
{
if(!parent)
{
parent = dat.GUI.autoPlaceContainer;
}
parent.removeChild(gui.domElement);
}
But gives back the error: cannot call method 'removeChild' of undefined, so i am guessing that autoPlaceContainer is wrong.
The original author of this function left these notes:
where the parameters gui represents the DAT.GUI you want to remove and parent is the parent container where if you didn't specify a domElement when instantiating DAT.GUI then you don't need to pass a parent.
var gui = new dat.GUI();
item = gui.add(text, 'message');
To delete:
gui.remove(item);
If your item is inside a folder, you have to do:
folder.remove(item);
If you want to remove the entire dat.GUI element along with all of its listeners, you can use gui.destroy()
If you want to reset the dat.GUI's values, you can use datGUI.__controllers.forEach(controller => controller.setValue(controller.initialValue));
You can delete dat.GUI element like this:
gui.remove()
You can try hiding it using:
gui.hide()
Change the remove function in the dat.gui.js file: "slice" wants to be "splice".
The answer can be found here: https://github.com/ulyssesp/dat.gui/commit/86f43c0be5db08c9a6d7339aa8287620306fb0b5
When I use:
loader = new MovieClipLoader();
_root.createEmptyMovieClip("level1",getNextHighestDepth());
_root.level1.createEmptyMovieClip("image",getNextHighestDepth());
loader.loadClip("http://someimage.jpg",_root.level1.image);
...it works and the image shows up.
But when I use:
loader = new MovieClipLoader();
_root.createEmptyMovieClip("level1",getNextHighestDepth());
_root.level1.createEmptyMovieClip("level2",getNextHighestDepth());
_root.level1.level2.createEmptyMovieClip("image",getNextHighestDepth());
loader.loadClip("http://someimage.jpg",_root.level1.level2.image);
...the image doesn't show up. Can anyone tell me why? How can I make this work?
the depth you are supplying is going to be '1'. is that what you are expecting?
each movie clip has it's own set of depths, so the nextHighestDepth() of the newly create 'image' mc, will be 1. it should not prevent loading the image though.
As gthmb says, you should call getNextHighestDepth() on the same MovieClip as you do the createEmptyMovieClip() on. So your code example should be more like:
loader = new MovieClipLoader();
_root.createEmptyMovieClip("level1",_root.getNextHighestDepth());
_root.level1.createEmptyMovieClip("level2",_root.level1.getNextHighestDepth());
_root.level1.level2.createEmptyMovieClip("image",_root.level1.level2.getNextHighestDepth());
loader.loadClip("http://someimage.jpg",_root.level1.level2.image);
Also, I would recommend storing references to the created MovieClips, so you won't have to use the full path in each occurrence in the code, something in the lines of this:
loader = new MovieClipLoader();
var level1:MovieClip = _root.createEmptyMovieClip("level1",_root.getNextHighestDepth());
var level2:MovieClip = level1.createEmptyMovieClip("level2",level1.getNextHighestDepth());
var image:MovieClip = level2.createEmptyMovieClip("image",level2.getNextHighestDepth());
loader.loadClip("http://someimage.jpg",image);