How can I get 3D object dimensions in ThreeJS? - json

I'm developing an AR application using WebRTC (webcam access), JSARToolKit (marker detection) and threeJS (3D library).
I want to place 3D objects (exported from Maya using threejs maya exporter) in the center of the detected marker.
This is the code where I load the 3D object using JSONLoader:
// load the model
var loader = new THREE.JSONLoader;
var object;
//var geometry = new THREE.BoxGeometry(1, 1, 1);
loader.load('js/cube.js', function(geometry, materials){
var material = new THREE.MeshFaceMaterial(materials);
object = new THREE.Mesh(geometry, material);
object.position.x -= ***3DobjectWidth/2***;
object.position.y -= ***3DobjectHeight/2***;
object.position.z -= ***3DobjectDepth/2***;
scene.add(object);
});
I need to get width, height and depth of the object to change his position (see 3DobjectWidth ecc).
Any suggestions?

The object size will be placed at geometry.boundingBox. But it has to be generated once.
try this.
geometry.computeBoundingBox();
var bb = geometry.boundingBox;
var object3DWidth = bb.max.x - bb.min.x;
var object3DHeight = bb.max.y - bb.min.y;
var object3DDepth = bb.max.z - bb.min.z;

Geometry has a .center() method. That might be more efficient and simpler if you need to center many meshes based on same geometry. It also calls computeBoundingBox for you.

Related

Controlling camera in Forge viewer

I'm trying to control the camera in the Autodesk Forge Viewer. Setting target and position seems to work fine, but if I try to set rotation or quaternion it do not have any effect.
To get the camera I use the getCamera function and then applyCamera after I have tried to set the parameters.
What I'm trying to achieve is to use the device orientation on a handheld device to rotate the model. Just using alpha and beta to set target is not a smooth experience.
// get camera
var cam = _viewer.getCamera();
// get position
var vecPos = cam.position;
// get view vector
var vecViewDir = new THREE.Vector3();
vecViewDir.subVectors(cam.target,cam.position);
// get length of view vector
var length = vecViewDir.length();
// rotate alpha
var vec = new THREE.Vector3();
vec.y = length;
var zAxis = new THREE.Vector3(0,0,1);
vec.applyAxisAngle(zAxis,THREE.Math.degToRad(alpha));
// rotate beta
var vec2 = new THREE.Vector3(vec.x,vec.y,vec.z);
vec2.normalize();
vec2.negate();
vec2.cross(zAxis);
vec.applyAxisAngle(vec2,THREE.Math.degToRad(beta) + Math.PI / 2);
// add to camera
cam.target.addVectors(vecPos,vec);
_viewer.applyCamera(cam,false);
You need to use the setView() method
_viewer.navigation.setView (pos, target) ;
and may also need to set the up vector to make sure you orient the camera the way you want.
_viewer.navigation.setCameraUpVector (upVector) ;

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.

Three.js JSONLoader blender model error: property 'length' undefined

I have been trying out Three.js lately and i used the exporter addon for Blender to test out making models in blender and exporting so i can use them in a three.js program.
I included the add-on to blender, and using just the basic cube model of blender, exported it to .json as the exporter says. Then i imported the model into my three.js using this as a guide
but that gave me an error:
Uncaught TypeError: Cannot read property 'length' of undefined.
Ive already searched online and tried a few different approaches(like including a materials in the function call of the loader) but nothing seems to work.
I also checked stackoverflow for answers but so far nothing seems solved. If anyone would clarify what im doing wrong I would be very grateful.
The code for my three.js program:
var WIDTH = 1000,
HEIGHT = 1000;
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
var radius = 50,
segments = 16,
rings = 16;
var sphereMaterial =
new THREE.MeshLambertMaterial(
{
color: 0xCCCCCC
});
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(
radius,
segments,
rings),
sphereMaterial);
var pointLight =
new THREE.PointLight(0x660000);
var $container = $('#container');
var renderer = new THREE.WebGLRenderer();
var camera =
new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR);
var scene = new THREE.Scene();
var loader = new THREE.JSONLoader(); // init the loader util
scene.add(camera);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight);
camera.position.z = 300;
renderer.setSize(WIDTH, HEIGHT);
$container.append(renderer.domElement);
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
loader.load('test.json', function (geometry, materials) {
var material = new THREE.MeshFaceMaterial(materials);
var object = new THREE.Mesh(geometry, material);
scene.add(object);
});
(function animloop() {
requestAnimFrame(animloop);
renderer.render(scene, camera);
})();
When you export, change the Type from buffergeometry to Geometry (Blender 2.76)
In my experience so far with the exporter, you have to be very careful which boxes you tick!
This length error is usually because you are either not exporting the vertexes, or exporting the scene rather the object.
I have the same issue with the new exporter.
If select SCENE then I usually get "Cannot read property 'length' of undefined"
If I select only the object and the materials and vertices. It usually just seems to get stuck forever.
UPDATE
Check this response to this issue!
Threejs blender exporter exports in wrong format
As roadkill stated earlier, exporting as Geometry instead of BufferedGeometry works.
Also note that loading JSON model loading is handled asyncronously (the callback is only called only when the operation is completed, the rest of your code keeps running). The model won't exist for the first frame or two, so check that your model exists before doing any operations with it inside the animation loop.

Why Box2D body doesn't collide?

I does some tests with Box2D and stuck with it.
Here is my body-construct code:
var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.fixedRotation = true;
var center:Number = Consts.stageToB2(Consts.worldSize / 2);
bodyDef.position.Set(center, center);
var body:b2Body = physicWorld.CreateBody(bodyDef);
var shape:b2CircleShape = new b2CircleShape(Consts.stageToB2(w) * 0.5); // our monster is in circle shape.
var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.shape = shape;
body.CreateFixture(fixtureDef);
I created such two bodies, but they doesn't collide! the debugDraw also doesn't light up the bodies. but when I add an angular velocitiy for one of them:
body.SetAngularVelocity(Math.PI / 89);
They'll start collide. Could you explain What happens here?
I have a feeling that Box2D won't check for collisions between objects that haven't had any forces applied to them. It seems logical because why would the engine waste resources to check those objects if they're not doing anything.
If you're placing the objects atop each other and then expecting something to happen without a gravity set or applying any forces to the boxes, that could be why.
What about gravity? If they are not moving at all , even not falling , it seems like you don't have a gravity in your world..
private var gravity:b2Vec2 = new b2Vec2(0, 9.8);
...
private var your_world:b2World = new b2World(gravity, true)

How to Save a Shape (or Sprite) to a Folder After Creation?

I am trying to create a county map of Illinois using x,y coordinates from a shp file. I have the x,y coordinates and the county names saved in a CSV file that I have no problem reading in via ActionScript 3 (and the map looks great), but in order to save time in my future application I would like to save each county shape as a permanent shape in my application and give them labels, i.e. Sprite1 (label='Champaign'). Is this possible? If so, any help would be greatly appreciated.
In case this is not possible I am trying an alternate solution: I create a new sprite (var spr:Sprite = new Sprite();) for each county, draw it using spr.graphics.drawPath, and give it a name (spr.name='Champaign') and then 'push' it to a vector of sprites (var xy_sprites:Vector. = new Vector.();). This would be great, but it doesn't work when I try to loop through each sprite in the vector and add an EventListener to that Sprite to pop up the name when you MouseOver any of the counties. Is the Sprite data type not the correct way to go about this or am a missing something about Sprites?
Some of my code to draw the shapes and save in a vector:
function drawXYMap(str:String):Vector.<Sprite> {
var arr:Array = str.split("\n");
var xy_Sprites:Vector.<Sprite> = new Vector.<Sprite>();
for (var i:int=0; i<arr.length-1; ++i) {
var spr:Sprite = new Sprite();
spr.graphics.lineStyle(1.0, 0x000000);
spr.graphics.beginFill(0x666699);
arr[i] = arr[i].split(',');
var xy_commands:Vector.<int> = new Vector.<int>();
var xy_coord:Vector.<Number> = new Vector.<Number>();
for (var j:int=1; j<arr[i].length; ++j) {
xy_coord.push(arr[i][j]*6);
if (j==1) {
xy_commands.push(1); // 1 is a move-to command
var cntry:String = arr[i][j-1] as String; //country name
}
else if (j % 2 == 1) {
xy_commands.push(2); // 2 is a line-to command
}
}
spr.graphics.drawPath(xy_commands, xy_coord);
spr.name = cntry;
xy_Sprites.push(spr);
addChild(spr);
}
return xy_Sprites;
}
But I can't seem to add an Event Listener to each sprite in the vector of sprites I created:
var str:String = csvLoader.data as String;
var xy_spr:Vector.<Sprite> = drawXYMap(str);
for each (var spr:Sprite in xy_spr) {
spr.addEventListener(MouseEvent.MOUSE_OVER,onOver);
}
function onOver(e:MouseEvent):void {
spr.alpha = .25;
}
Any help would be great.
Thanks!
You could put all of your x, y co-ords into a Vector data structure, and then use the graphics.drawPath() method, to iterate over the co-ords any time you instantiate the county.
If you created a class that extended Shape, you could add a Vector that references the CSV file, and every time you create the shape, it automatically draws the path.
Check out this useful document from Adobe:
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WSA8BD9022-BAB1-46d3-9B26-0D9649743C8E.html
PS don't forget to use the proper drawing commands for the drawPath(). You should be able to interact with it as normal if you do.