Raycasting against 'subscenes' in three.js - html

So I am working with the webgl_interactive_cubes.html in the three.js examples, and I have a relatively simple question: Is it possible to test for intersection of a ray with the children of an object.
for example, if I do something like:
for ( var i = 0; i < 2000; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
scene.add( object );
}
When I call
var intersects = raycaster.intersectObjects( scene.children );
It will intersect these objects. However if I first create a 'subScene' like so:
var subScene = new THREE.Object3D();
scene.add(subScene);
And then add all of these objects to the subScene instead of the scene, the intersection will no longer occur.
Is it at all possible to intersect ALL the objects in the scene and subscenes ( in the final project I would like to have many layers of nested subScenes ) Or should I try to keep all objects in the same scene if I am using raycasting?
Thank you in advance for your time,
Isaac

You just need to set the recursive flag:
var intersects = raycaster.intersectObjects( scene.children, true );
three.js r.58

Related

How to smooth mesh triangles in STL loaded BufferGeometry

I´m trying to load some STL files using Three.js. The models are loaded correctly, but there are too many triangles that I would like to merge/smooth.
I had successfully applied smooth loading terrains in other 3D formats, but I can´t do it with the BufferGeometry that results from loading an STL file with the STLLoader.
_
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeBoundingSphere();
object.computeFaceNormals();
object.computeVertexNormals();
object.normalizeNormals();
object.center();
// Apply smooth
var modifier = new THREE.SubdivisionModifier( 1);
var smooth = smooth = object.clone();
smooth.mergeVertices();
smooth.computeFaceNormals();
smooth.computeVertexNormals();
modifier.modify( smooth );
scene.add( smooth );
});
This is what I tried, it throws an error: Uncaught TypeError: smooth.mergeVertices is not a function
If I comment the "mergeVertices()" line, what I get is a different error: Uncaught TypeError: Cannot read property 'length' of undefined in SubdivisionsModifier, line 156.
It seems that the sample codes I´m trying are outdated (this is happenning a lot recently due to the massive changes in the Three.JS library). Or maybe I´m forgetting something. The fact is that the vertices seems to be null..?
Thanks in advance!
It seems I was looking in the wrong direction: smoothing the triangles has nothing to do with the SubdivisionsModifier... What I needed was easier than that, just compute the vertex BEFORE applying the material, so it can use SmoothShading instead of FlatShading (did I got it right?).
The problem here was that the BufferGeometry returned by the STLLoader has not calculated vertices/vertex, so I had to do it manually. After that, apply mergeVertices() just before computeVertexNormals() and voilà! The triangles dissappear and everything is smooth:
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeVertexNormals();
object.center();
///////////////////////////////////////////////////////////////
var attrib = object.getAttribute('position');
if(attrib === undefined) {
throw new Error('a given BufferGeometry object must have a position attribute.');
}
var positions = attrib.array;
var vertices = [];
for(var i = 0, n = positions.length; i < n; i += 3) {
var x = positions[i];
var y = positions[i + 1];
var z = positions[i + 2];
vertices.push(new THREE.Vector3(x, y, z));
}
var faces = [];
for(var i = 0, n = vertices.length; i < n; i += 3) {
faces.push(new THREE.Face3(i, i + 1, i + 2));
}
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices()
geometry.computeVertexNormals();
///////////////////////////////////////////////////////////////
var mesh = new THREE.Mesh(geometry, material);
scene.add( mesh );
});
Than, you can convert it back to BufferGeometry, because it's more GPU/CPU efficient for more complex models:
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
var buffer_g = new THREE.BufferGeometry();
buffer_g.fromGeometry(geometry);
var mesh = new THREE.Mesh(buffer_g, material);
scene.add( mesh )
Happened this issue for me while loading an obj file. If you have a 3d software like 3dsmax:
Open the obj file,
Go to polygons selection mode and select all polygons.
Under the Surface properties panel, click 'Auto Smooth' button.
Export the model back to obj format
Now you won't have to call the functions geometry.mergeVertices() and geometry.computeVertexNormals();. Just load the obj and add to the scene, mesh will be smooth.
EDIT:
My obj files had meshphongmaterial by default and on changing the shading property to value 2 the mesh became smooth.
child.material.shading = 2
STL does not support vertex index.
That is reason it has duplicated vertex of all triangles.
Each vertex has its normal as triangle normal.
As a result, at same position( multiple very closed vertices), there is multiple normal value.
This leads to non-smooth surface of geometry when using Normal for lighting calculation.

How to group json models in THREE.js?

I wish to group loaded json models in THREE.js but am getting an error...
THREE.Object3D.add: object not an instance of THREE.Object3D
I'm using the example code of...
var group = new THREE.Object3D();
and then...
group.add(model1);
group.add(model2);
I load model1 and model2 previously - these render out fine when I add them to the scene individually, but when I add the group I get the error...
scene.add( group );
I've tried a combination of different techniques such as THREE.Mesh() or adding json models to other json models eg. model1.add(model2).
R72
The problem is asynchronous loading of the models - trying to perform actions on the models before they are loaded.
group = new THREE.Mesh(); //global to add transforms
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load( "model1.json", function(geometry,materials) {
var mesh = createMesh(geometry,materials); //function to create generic mesh properties
group.add(mesh);
});
jsonLoader.load( "model2.json", function(geometry,materials) {
var mesh = createMesh(geometry,materials); //function to create generic mesh properties
group.add(mesh);
});
scene.add( group );
function createMesh( geometry, materials )
{
var material = new THREE.MeshFaceMaterial( materials );
mesh = new THREE.Mesh( geometry, material );
return mesh;
};

AS3 - Using a For Loop to Update Multiple Points and Their Values in an Array

I'm a bit new with AS3 (but not really with coding) so please forgive my ignorance. I'm creating a small function that will be called by a Main Function to update the position of 52 Pointers that have the x and y position of multiple point objects (empty movie clips). It will also then update two global arrays with those values (one array for the x and one for the y).
The problem is, as there are 52 of them, and they will probably grow in quantity, I'd like to be able to use a FOR function to do it, but I can't seem to be able to figure it out.
I get this error: Access of undefined property _point.
Here is a piece of the code that dream about:
function happyFunc():void
{
var avpointers:int = 52;
var vpointx:Array = new Array();
var vpointy:Array = new Array();
for (aa=0; aa<vpointers; aa++)
{
vpointx[aa] = _point[aa].x;
vpointy[aa] = _point[aa].y;
}
}
And this is the code that I'm stuck with...
function reallySadFunc():void
{
_point1 = localToGlobal(new Point(point1.x,point1.y));
//...
_point52 = localToGlobal(new Point(point52.x,point1.y));
vpointx[0] = _point1.x;
vpointx[1] = _point2.x;
//...
//oh god there are 104 lines of this why do I have to suffer
}
Thank you!
If I read your question correctly, I think this is what you need:
public static const CLIP_COUNT:int = 52;
// ...
private function happyFunc(parentClip:DisplayObjectContainer):void
{
var vpointx:Vector.<Number> = new Vector.<Number>(CLIP_COUNT, true);
var vpointy:Vector.<Number> = new Vector.<Number>(CLIP_COUNT, true);
var clipPoint:Point = new Point ();
var globalPoint:Point;
for (var i:int = 0; i < CLIP_COUNT; i++)
{
var childClip:DisplayObject = parentClip.getChildByName("point" +
(i + 1).toString());
clipPoint.x = childClip.x;
clipPoint.y = childClip.y;
globalPoint = parentClip.localToGlobal(clipPoint);
vpointx[i] = globalPoint.x;
vpointy[i] = globalPoint.y;
}
// do something with vpointx and vpointy - they're local variables
// and will go out of scope unless you declare them as class members
// or somehow return them from this function.
}
This function works by taking the parent display object (the one that contains the 52 movie clips - this could be the Stage) and iterates through them by getting each movie clip by name. The code assumes that your movie clips are called point1, point2, ..., point52.
To speed up the local-to-global coordinate conversion, two Point objects are created and then reused during the for loop to avoid creating many temporary Point instances.
Instead of using Array, use Vector.<Number> - this class has better performance than Array does.

Two dimensional array of objects in as3

Is it possible to make array in as3 like this :
countdowns[bodyID][powerupName] = { time: powerup.getTime(), onRemove: onRemove };
I have been trying for hours but no luck..
Thanks
Sure, but you have to declare each object separately and you have to call index number instead of variable name
Say I have an array of colors with a sub array of types of that color filled with objects describing RGB
var colors:Array = [];
var red:Array = [];
var darkRed:Object = { r : 256, g : 100, b : 100 }
red.push( darkRed ); //darkRed is now part of red
colors.push( red ); //red is now part of colors
To access darkRed, you would do this:
colors[0][0]; //that is darkRed
I believe this is what you are trying to accomplish :
var countdowns:Array = new Array;
countdowns[bodyID] = new Array;
countdowns[bodyID][powerupName] = { time: powerup.getTime(), onRemove: onRemove };
The second dimension of the countdowns Array is achieved by inserting an array into each element of the first dimension.
While this certainly can be done, but it's possible you're overcomplicating things. Perhaps a single-dimensional Array ob tailored Objects will suit better.
var ob:Object=new Object();
ob.bodyId=bodyId;
ob.powerup=powerup; // direct link to powerup might serve you better!
ob.onRemove=onRemove;
countdowns.push(ob);

AS3 Can't access MC on Stage

I have 54 MC's on stage, generated dynamically. Now I want to get their x and y positions when rolling over but I am having problems getting the path correct e.g.
function copyFlightCellData():void {
var r; var s;
var cellData:Array = new Array ();
for (r = 0; r < 54; r++){
//var copyCellData = new MovieClip();
cellData[r] = Object(root).mc85.name; //["mc"+r+r];
trace("$$$$$$$$$$$$$$$$$$$$$" + cellData[r]);
}
}
I used the list objects in debug and they are listed in _level0 e.g.
Movie Clip: Frame=1 Target="_level0.mc85"
Not sure why I can't access their properties.
This is the code that created the MC's
// Create copies of flightCell for board grid
var my_mc = new flightCell();
my_mc.name = "mc" + i + j;
trace("^^^^^^^^^^^^^^****************" + my_mc.name);
addChild(my_mc);
Answer is pretty simple, use the DisplayObjectContainer object's, in this case root, getChildByName() method, for example:
var sprite1:Sprite = new Sprite();
sprite1.name = "sprite1";
addChild(sprite1);
trace((root as DisplayObjectContainer).getChildByName("sprite1").name); // output : sprite1
It's probably a better idea to store the movieclips you have on your stage in an array to begin with.
To access it by name you have to assign a name to them when you create them.
mc85.name = "mc85";
As an alternative that I recommend, you can use getChildAt(index) : http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObjectContainer.html?filter_flash=cs5&filter_flashplayer=10.2&filter_air=2.6#getChildAt()
Also, I highly recomend you to create an empty movieclip or sprite and add all of this mcs to them instead of the root.