Three js hiding multiple objects just hide single object - javascript

I was running into a problem when I was setting my light objects name light.name = 'globalLight' and im making variable that is lightGlobal = scene.getObjectByName('globalLight'); but when i tried to hide using lightGlobal.visible = false; it was just let the other light wasn’t hide the green one Is light0 and the blue one is light1 render example The rendered image example
The problem screenshot
As you can see the blue light is gone but the green one won’t hide
My code :
ambient = new THREE.AmbientLight( 0x404040 );
scene.add( ambient );
lightCam = new THREE.DirectionalLight( 0xffffff, 1.0 );
light0 = new THREE.PointLight( 0x0000ff, 1.0 );
light0.position.set( 4.0, 4.0, 4.0 );
light0.castShadow = true;
light0.name = "globalLight";
light1 = new THREE.PointLight( 0x00ff00, 1.0 );
light1.position.set( 4.0, 4.0, -4.0 );
light1.castShadow = true;
light1.name = "globalLight";
scene.add( light0, light1 );
globalLight = scene.getObjectByName( "globalLight", true );
globalLight.visible = false;

Object3D.name has to be unique. Hence, naming two lights in the same way is not supported. Object3D.getObjectByName() will return the first object that matches the given name.
One solution for this problem is to use Object3D.userData and define a custom property like:
light0.userData.tag = 'globalLight';
You can then consider to enhance Object3D by the following method:
THREE.Object3D.prototype.getObjectsByTag = function( tag, result ) {
// check the current object
if ( this.userData.tag === tag ) result.push( this );
// check children
for ( let i = 0, l = this.children.length; i < l; i ++ ) {
const child = this.children[ i ];
child.getObjectsByTag( tag, result );
}
return result;
};

Related

Photoshop / Take the color out of the selection

Please help in solving the following problem:
I need to pick up color from a selected randomly shaped area, probably quite small relative to the image and having no known position on the canvas beforehand, with an eyedropper.
This needs to be done automatically, without manual clicks.
With my skills, I was only able to write a script that takes a sample from the center of the current document, which doesn't solve the problem, but it gives direction.
// Set reference for active document
var srcDoc = app.activeDocument;
// remove any sample first
srcDoc.colorSamplers.removeAll();
// get width and height of image
var w = srcDoc.width.value;
var h = srcDoc.height.value;
// get positions of the center of the image
//var x = 0;
//var y = 0;
var x = Math.round(w/2);
var y = Math.round(h/2);
// will pick a sample from the middle of the image
var px = [UnitValue(x) , UnitValue(y)];
var Sampler = srcDoc.colorSamplers.add(px);
// Copy RGB Values of current layer with 3 decimal spaces
var myColor = Sampler.color;
var rgb_R = Math.round(myColor.rgb.red*1000)/1000;
var rgb_G = Math.round(myColor.rgb.green*1000)/1000;
var rgb_B = Math.round(myColor.rgb.blue*1000)/1000;
set(rgb_R, rgb_G, rgb_B, "photoshopPicker");
function set(red, Grn, blue, source) {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var reference = new ActionReference();
reference.putProperty( s2t( "color" ), s2t( "foregroundColor" ));
descriptor.putReference( c2t( "null" ), reference );
descriptor2.putDouble( s2t( "red" ), red );
descriptor2.putDouble( c2t( "Grn " ), Grn );
descriptor2.putDouble( s2t( "blue" ), blue );
descriptor.putObject( s2t( "to" ), s2t( "RGBColor" ), descriptor2 );
descriptor.putString( s2t( "source" ), source );
executeAction( s2t( "set" ), descriptor, DialogModes.NO );
}
I haven't found ways to do this in PS itself without human involvement and I have no experience in scripting, but scripting seems to be the correct solution to the problem. It would be great to have a ready-made script for such an operation.
Thank you.

THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute unindexBufferGeometry

Would really appreciate some help updating the webgl-wireframes library code to the latest version of threejs.
This function causes the following errors
Uncaught TypeError: THREE.Geometry is not a constructor
THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry
.setAttribute to replace/resize attribute buffers
Library with implementation: https://github.com/mattdesl/webgl-wireframes.
Thanks to Mugen87, this code now works for me in place of the helper functions with the original libray.
function createGeometry ( edgeRemoval, x_divisions, y_divisions) {
if (mesh.geometry) mesh.geometry.dispose();
geometry = new THREE.PlaneBufferGeometry(3, 3, x_divisions, y_divisions)
geometry = geometry.toNonIndexed();
const pos = geometry.attributes.position
const count = pos.length / 3
let bary = []
const removeEdge = edgeRemoval
for (let i = 0; i < count; i++){
const even = i % 2 === 0
const Q = removeEdge ? 1 : 0
if (even) {
bary.push(0, 0, 1,
0, 1, 0,
1, 0, Q )
} else {
bary.push(0, 1, 0,
0, 0, 1,
1, 0, Q
)
}
}
bary = new Float32Array(bary)
geometry.setAttribute(
"barycentric",
new THREE.BufferAttribute(bary, 3)
)
mesh.geometry = geometry;
mesh.material = material;
}
webgl-wireframes requires non-indexed geometries so barycentric coordiantes can be computed for the wireframe effect. Hence, the project developed the helper function unindexBufferGeometry().
With the latest version of three.js (r128) the library could use BufferGeometry.toNonIndexed() which does not throw the above error. So this line should be:
geometry = geometry.toNonIndexed();
Notice that setArray() was removed because it was possible to use this method to resize buffer attributes. This workflow is not supported anymore since buffer attributes are considered to have a fixed size (for performance reasons). So if you want to resize buffer data, create a new geometry with new buffer attributes.

three.js - How to have non visual attributes connected to objects?

I have a rather broad question, but no idea how to tackle that. So forgive me.
I am trying to have several (like 200 and more) objects and, let's just say, a container to the side of the field, where I draw the objects. Now what I want is, that each object has some non visual attributes and when I click on that object, the attributes should appear in that container.
Now I could go about it?
I mean, I know I can ask for the name of the selected object and then do a key value query from some dictionary. Question is, whether there is an easier way to go about it.
For the click event I used a library called threex.domevents, check the GitHub page for more information, the code for the event it's self explanatory.
First domevents needs to be initialized in your scene like this:
var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
Then I created a custom Mesh object:
// random id
function genRandomId()
{
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
// random int for position
var min = -50;
var max = 50;
function genRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// custom mesh --------------------------------------------
function MyMesh(geometry, material, destinationContainer) {
THREE.Mesh.call(this, geometry, material);
this.userData = {
foo1: genRandomId(),
foo2: genRandomId(),
foo3: genRandomId(),
};
this.position.x = genRandomInt(min, max);
this.position.y = genRandomInt(min, max);
this.position.z = genRandomInt(min, max);
var that = this;
// click event listener
domEvents.addEventListener(this, 'click', function(event) {
console.log('clicked object on position:');
console.log(that.position);
destinationContainer.userData = that.userData;
console.log('Now the conainer has:');
console.log(destinationContainer.userData);
destinationContainer.userData = that.userData;
}, false);
}
MyMesh.prototype = Object.create(THREE.Mesh.prototype);
MyMesh.prototype.constructor = MyMesh;
genRandomId and genRandomInt are random generators for the pourpose of illustrating this example, I took the code for the random ids from Generate random string/characters in JavaScript.
In your scene you can generate 200 (or more) MyMesh meshes and add them to the scene:
const color = 0x156289;
const emissive = 0x072534;
var planeGeometry = new THREE.PlaneGeometry(5, 5);
var planeMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
side: THREE.DoubleSide,
shading: THREE.FlatShading
});
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh);
var objGeometry = new THREE.BoxGeometry(1, 1, 1);
var objMaterial = new THREE.MeshPhongMaterial({
color: color,
emissive: emissive,
shading: THREE.FlatShading
});
var i = 0;
while (i < 200) {
scene.add(new MyMesh(objGeometry, objMaterial, planeMesh));
i++;
}
And finally render the scene:
var render = function() {
requestAnimationFrame(render);
planeMesh.rotation.x += 0.010;
planeMesh.rotation.y += 0.010;
renderer.render(scene, camera);
};
render();
This is a demo with the full source code: http://run.plnkr.co/plunks/W4x8XsXVroOaLUCSeXgO/
Open the browser console and click on a cube and you'll see the that planeMesh is switching its userData attributes with the ones of the clicked cube mesh.
Yes, that's fine. You can put your own custom keys directly on a Three.js object and it shouldn't bother it as long as you don't accidentally overwrite an important built-in Three.js key. For that reason I'd recommend that you put all of your custom keys in a "namespace" on the object so they're nice and neat and contained.
For example, if you had a Three.js object foo, you could put all your keys under foo.myCustomNamespace, so that your custom data like foo.myCustomNamespace.name, foo.myCustomNamespace.description, etc. are all together and won't interfere with THREE.js properties.
Edit: Three.js provides a built-in namespace for user data called, conveniently, userData. Access it on THREE.Object3D.userData.
https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L92

delete child not work it just delete half of them

In this code I can't remove all children if I delete half of them?? and I got error child is not dignified while i define it? each node has children spheres (nods) and edges (lines) this method delete only nods, why? could any one help me please?
function onMouseClick( e ) {
mouseVector.x = 2 * (e.clientX / containerWidth) - 1;
mouseVector.y = 1 - 2 * ( e.clientY / containerHeight );
var raycaster = projector.pickingRay( mouseVector.clone(), camera ),
intersects = raycaster.intersectObjects( scene.children );
for( var i = 0; i < intersects.length; i++ ) {
//INTERSECTED = intersects[0].object;
INTERSECTED = intersects[i].object;
//obj = intersection.object;
alert(INTERSECTED.id);
/*1-*/ //this
//scene.remove(INTERSECTED);
/*2-*/ //or this
for ( c = 0, cl = INTERSECTED.children.length; c < cl; c ++ ) {
var child = INTERSECTED.children[ c ];
alert(child.id);
//child.parent.remove(obj);
INTERSECTED.remove(child);
//var lin = scene.children[child.id+1];
//r lin = scene.getObjectById(child.id+1, true );
// alert(child.id+1);
// INTERSECTED.remove(scene.children[child.id+1]);
}
//scene.remove(INTERSECTED);
scene.add(INTERSECTED);
animate();
}
}
I think that the problem lies in the fact that you loop an array of which you are changing the length. This has nothing to do with three.js but is a basic (beginners) error you made in your javascript code.
Maybe you should rewrite your code and use a while loop instead:
while( INTERSECTED.children.length > 0 ){
var child = INTERSECTED.children[ 0 ];
INTERSECTED.remove(child);
}

Three.js - load JSON model once and add it multiple times

Is it possible to load a JSON model once and add it to the scene multiple times with different scales, positions, etc?
If I add the Object3D() to an array, give a position and scale to the object in the array, add it to the scene, and then repeat this process, the position and scale are overwritten for every object in the array.
I can't think of anything that works, so I'm hoping someone could give me a working example of what I'm trying to accomplish.
Here's (one of many) of my failed attempts. Should give you a basic idea of what I'm trying to do, if my explanation wasn't sufficient.
function addModels(){
var models = [];
var model = new THREE.Object3D();
var modelTex = THREE.ImageUtils.loadTexture( "textures/model.jpg" );
var loader = new THREE.JSONLoader();
loader.load( "models/model.js", function( geometry ) {
mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { map: modelTex }) );
model.add(mesh);
} );
for(var i = 0; i < 5; i++){
model.scale.set(i,i,i);
model.position.set(i,i,i);
models[i] = model;
scene.add(models[i]);
}
}
You need to clone a model first and then set position and scale.
for(var i = 0; i < 5; i++){
var newModel = model.clone();
newModel.position.set(i,i,i);
newModel.scale.set(i,i,i);
scene.add(newModel);
}
Updated: Example how you can create json model without load : Fiddle example or just simple add loop inside load function.
You can create new meshes from the same geometries and materials:
loader.load( "models/model.js", function( geometry ) {
var mat = new THREE.MeshLambertMaterial( { map: modelTex });
for (var i = 0; i < 5; i++) {
var mesh = new THREE.Mesh( geometry, mat );
mesh.position.set(i, i, i);
mesh.scale.set(i, i, i);
scene.add(mesh);
}
});

Categories

Resources