three;js: Error accessing object - javascript

Basically I'm trying to get a 3d cube to face the direction the mouse is in. It's almost there, but right now it's not rendering the cube, which it was doing fine before I added this code:
cube.look(xTarget, yTarget);
which is giving this error:
Uncaught TypeError: Cannot read property 'look' of undefined`
It's making the cube object inaccessible, why is that? (...at least, that's what I think the problem is). What am I doing wrong here?
Here's a plunker
Here's the relevant js:
Cube.prototype.updateBody = function(speed){
this.box.rotation.y += (this.tBoxRotY - this.box.rotation.y) / speed;
this.box.rotation.x += (this.tBoxRotX - this.box.rotation.x) / speed;
this.box.position.x += (this.tBoxPosX-this.box.position.x) / speed;
this.box.position.y += (this.tBoxPosY-this.box.position.y) / speed;
this.box.position.z += (this.tBoxPosZ-this.box.position.z) / speed;
}
Cube.prototype.look = function(xTarget, yTarget){
this.tBoxRotY = rule3(xTarget, -200, 200, -Math.PI/4, Math.PI/4);
this.tBoxRotX = rule3(yTarget, -200,200, -Math.PI/4, Math.PI/4);
this.tBoxPosX = rule3(xTarget, -200, 200, 70,-70);
this.tBoxPosY = rule3(yTarget, -140, 260, 20, 100);
this.tBoxPosZ = 0;
}
function loop() {
render();
var xTarget = (mousePos.x-windowHalfX);
var yTarget= (mousePos.y-windowHalfY);
console.log('Mouse X position: ' + xTarget +', Y Target = '+yTarget );
cube.look(xTarget, yTarget);
requestAnimationFrame(loop);
}

Working plunker here. http://plnkr.co/edit/3gZVI8UXRdTW7fLddj9N?p=preview
There were several problems
I changed
init();
animate();
loop();
createCube();
to
init();
createCube();
animate();
loop();
in order to fix your null reference problems. (Animate and loop require the cube to be created before they can work with it).
Also your inheritance (I assume you were going for inheritance?) was incorrect.
I updated it to
Cube = function(){
var geometry = new THREE.BoxGeometry( 50, 50, 50 );
for ( var i = 0; i < geometry.faces.length; i += 2 ) {
var hex = Math.random() * 0xffffff;
geometry.faces[ i ].color.setHex( hex );
geometry.faces[ i + 1 ].color.setHex( hex );
}
var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5 } );
//I removed this line
//Can't do inheritance like this as far as I know?
//return box = new THREE.Mesh( geometry, material );
//And added this line instead.
//Apply your arguments to the Mesh's constructor
THREE.Mesh.apply(this, [geometry, material]);
}
//I added these lines as well...
//Set up the prototypes and constructors for inheritance
Cube.prototype = THREE.Mesh.prototype;
Cube.prototype.constructor = Cube;
Also updated Cube.prototype.updateBody to appropriately call the inherited Mesh's rotation (this.rotation.x as opposed to this.box.rotation.x)

Related

THREE.js Thick Arrow with lookAt() capability

I wanted to make a "Thick Arrow" mesh i.e. an arrow like the standard Arrow Helper but with the shaft made out of a cylinder instead of a line.
tldr; do not copy the Arrow Helper design; see the Epilogue section at end of the question.
So I copied and modified the code for my needs (dispensed with constructor and methods) and made the changes and now it works OK:-
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//... START of ARROWMAKER SET of FUNCTIONS
// adapted from https://github.com/mrdoob/three.js/blob/master/src/helpers/ArrowHelper.js
//====================================
function F_Arrow_Fat_noDoesLookAt_Make ( dir, origin, length, shaftBaseWidth, shaftTopWidth, color, headLength, headBaseWidth, headTopWidth )
{
//... dir is assumed to be normalized
var thisArrow = new THREE.Object3D();////SW
if ( dir === undefined ) dir = new THREE.Vector3( 0, 0, 1 );
if ( origin === undefined ) origin = new THREE.Vector3( 0, 0, 0 );
if ( length === undefined ) length = 1;
if ( shaftBaseWidth === undefined ) shaftBaseWidth = 0.02 * length;
if ( shaftTopWidth === undefined ) shaftTopWidth = 0.02 * length;
if ( color === undefined ) color = 0xffff00;
if ( headLength === undefined ) headLength = 0.2 * length;
if ( headBaseWidth === undefined ) headBaseWidth = 0.4 * headLength;
if ( headTopWidth === undefined ) headTopWidth = 0.2 * headLength;//... 0.0 for a point.
/* CylinderBufferGeometry parameters from:-
// https://threejs.org/docs/index.html#api/en/geometries/CylinderBufferGeometry
* radiusTop — Radius of the cylinder at the top. Default is 1.
* radiusBottom — Radius of the cylinder at the bottom. Default is 1.
* height — Height of the cylinder. Default is 1.
* radialSegments — Number of segmented faces around the circumference of the cylinder. Default is 8
* heightSegments — Number of rows of faces along the height of the cylinder. Default is 1.
* openEnded — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.
* thetaStart — Start angle for first segment, default = 0 (three o'clock position).
* thetaLength — The central angle, often called theta, of the circular sector. The default is 2*Pi, which makes for a complete cylinder.
*/
//var shaftGeometry = new THREE.CylinderBufferGeometry( 0.0, 0.5, 1, 8, 1 );//for strongly tapering, pointed shaft
var shaftGeometry = new THREE.CylinderBufferGeometry( 0.1, 0.1, 1, 8, 1 );//shaft is cylindrical
//shaftGeometry.translate( 0, - 0.5, 0 );
shaftGeometry.translate( 0, + 0.5, 0 );
//... for partial doesLookAt capability
//shaftGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
var headGeometry = new THREE.CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); //for strongly tapering, pointed head
headGeometry.translate( 0, - 0.5, 0 );
//... for partial doesLookAt capability
//headGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
thisArrow.position.copy( origin );
/*thisArrow.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
thisArrow.line.matrixAutoUpdate = false;
thisArrow.add( thisArrow.line ); */
thisArrow.shaft = new THREE.Mesh( shaftGeometry, new THREE.MeshLambertMaterial( { color: color } ) );
thisArrow.shaft.matrixAutoUpdate = false;
thisArrow.add( thisArrow.shaft );
thisArrow.head = new THREE.Mesh( headGeometry, new THREE.MeshLambertMaterial( { color: color } ) );
thisArrow.head.matrixAutoUpdate = false;
thisArrow.add( thisArrow.head );
//thisArrow.setDirection( dir );
//thisArrow.setLength( length, headLength, headTopWidth );
var arkle = new THREE.AxesHelper (2 * length);
thisArrow.add (arkle);
F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir ) ;////SW
F_Arrow_Fat_noDoesLookAt_setLength ( thisArrow, length, headLength, headBaseWidth ) ;////SW
F_Arrow_Fat_noDoesLookAt_setColor ( thisArrow, color ) ;////SW
scene.add ( thisArrow );
//... this screws up for the F_Arrow_Fat_noDoesLookAt kind of Arrow
//thisArrow.lookAt(0,0,0);//...makes the arrow's blue Z axis lookAt Point(x,y,z).
}
//... EOFn F_Arrow_Fat_noDoesLookAt_Make().
//=============================================
function F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir )
{
// dir is assumed to be normalized
if ( dir.y > 0.99999 )
{
thisArrow.quaternion.set( 0, 0, 0, 1 );
} else if ( dir.y < - 0.99999 )
{
thisArrow.quaternion.set( 1, 0, 0, 0 );
} else
{
const _axis = /*#__PURE__*/ new THREE.Vector3();
_axis.set( dir.z, 0, - dir.x ).normalize();
const radians = Math.acos( dir.y );
thisArrow.quaternion.setFromAxisAngle( _axis, radians );
}
}
//... EOFn F_Arrow_Fat_noDoesLookAt_setDirection().
//=========================================
function F_Arrow_Fat_noDoesLookAt_setLength( thisArrow, length, headLength, headBaseWidth )
{
if ( headLength === undefined ) headLength = 0.2 * length;
if ( headBaseWidth === undefined ) headBaseWidth = 0.2 * headLength;
thisArrow.shaft.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
//x&z the same, y as per length-headLength
//thisArrow.shaft.position.y = length;//SW ???????
thisArrow.shaft.updateMatrix();
thisArrow.head.scale.set( headBaseWidth, headLength, headBaseWidth ); //x&z the same, y as per length
thisArrow.head.position.y = length;
thisArrow.head.updateMatrix();
}
//...EOFn F_Arrow_Fat_noDoesLookAt_setLength().
//========================================
function F_Arrow_Fat_noDoesLookAt_setColor( thisArrow, color )
{
thisArrow.shaft.material.color.set( color );
thisArrow.head.material.color.set( color );
}
//...EOFn F_Arrow_Fat_noDoesLookAt_setColor().
//... END of ARROWMAKER SET of FUNCTIONS
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
This works OK for a fixed-direction arrow where the arrow direction can be supplied at time of construction.
But now I need to change the arrow orientation over time (for tracking a moving target). Currently the Object3D.lookAt() function is not sufficient because the arrow points along its Object3D y-axis, whereas lookAt() orients the Object3D z-axis to look at the given target position.
With experimentation I have gotten part-way there by using:-
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
on the shaft and head geometries (the 2 lines are commented out in the above code extract). This seems to get the cylinder meshes pointing in the correct direction. But the problem is that the meshes are mis-shaped and the head mesh is displaced away from the shaft mesh.
With trial and error I might be able to adjust the code to get the arrow to work for my present example. But (given my weak understanding of quaternions) I am not confident that it would (a) be general enough to apply in all situations or (b) be sufficiently future-proof against evolution of THREE.js.
So I would be grateful for any solutions/recommendations on how to achieve the lookAt() capability for this "Thick Arrow".
Epilogue
My main takeaway is NOT to follow the design of the Helper Arrow.
As TheJim01's and somethinghere's answers indicate, there is an easier approach using the Object3D.add() "nesting" function.
For example:-
(1) create two cylinder meshes (for arrowshaft and arrowhead) which by default will point in the Y-direction; make geometry length =1.0 to assist future re-scaling.
(2) Add the meshes to a parent Object3D object.
(3) Rotate the parent +90 degrees around the X-axis using parent.rotateX(Math.PI/2).
(4) Add the parent to a grandparent object.
(5) Subsequently use grandparent.lookAt(target_point_as_World_position_Vec3_or_x_y_z).
N.B. lookAt() will not work properly if parent or grandparent have scaling other than (n,n,n).
The parent and grandparent object types may be plain THREE.Object3D, or THREE.Group, or THREE.Mesh (made invisible if required e.g. by setting small dimensions or .visibility=false)
Arrow Helper can be used dynamically but only if the internal direction is set to (0,0,1) before using lookAt().
You can apply lookAt to any Object3D. Object3D.lookAt( ... )
You have already discovered that lookAt causes the shapes to point in the +Z direction, and are compensating for that. But it can be taken a step further with the introduction of a Group. Groups are also derived from Object3D, so they also support the lookAt method.
let W = window.innerWidth;
let H = window.innerHeight;
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(28, 1, 1, 1000);
camera.position.set(10, 10, 50);
camera.lookAt(scene.position);
scene.add(camera);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 0, -1);
camera.add(light);
const group = new THREE.Group();
scene.add(group);
const arrowMat = new THREE.MeshLambertMaterial({color:"green"});
const arrowGeo = new THREE.ConeBufferGeometry(2, 5, 32);
const arrowMesh = new THREE.Mesh(arrowGeo, arrowMat);
arrowMesh.rotation.x = Math.PI / 2;
arrowMesh.position.z = 2.5;
group.add(arrowMesh);
const cylinderGeo = new THREE.CylinderBufferGeometry(1, 1, 5, 32);
const cylinderMesh = new THREE.Mesh(cylinderGeo, arrowMat);
cylinderMesh.rotation.x = Math.PI / 2;
cylinderMesh.position.z = -2.5;
group.add(cylinderMesh);
function render() {
renderer.render(scene, camera);
}
function resize() {
W = window.innerWidth;
H = window.innerHeight;
renderer.setSize(W, H);
camera.aspect = W / H;
camera.updateProjectionMatrix();
render();
}
window.addEventListener("resize", resize);
resize();
let rad = 0;
function animate() {
rad += 0.05;
group.lookAt(Math.sin(rad) * 100, Math.cos(rad) * 100, 100);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
background: skyblue;
}
<script src="https://threejs.org/build/three.min.js"></script>
The key here is that the cone/shaft are made to point in the +Z direction, and then added to the Group. This means their orientations are now local to the group. When the group's lookAt changes, the shapes follow suit. And because the "arrow" shapes point in the group's local +Z direction, that means they also point at whatever position was given to group.lookAt(...);.
Further work
This is just a starting point. You'll need to adapt this to how you want it to work with constructing the arrow at the correct position, with the correct length, etc. Still, the grouping pattern should make lookAt easier to work with.
All you require is some more understanding of nesting, which allows you to place objects relative to their parents. As mentioned in the answer above, you could use Group or Object3D, but you don't have to. You can just nest your arrowhead on your cylinder and point your cylinder into the z-direction, then use the built-in, dont-overcomplicate-things methods lookAt.
Try not to use matrices or quaternions for simple things like this, as it makes for a way harder time figuring things out. Since THREE.js allows for nested frames, make use of that!
const renderer = new THREE.WebGLRenderer;
const camera = new THREE.PerspectiveCamera;
const scene = new THREE.Scene;
const mouse = new THREE.Vector2;
const raycaster = new THREE.Raycaster;
const quaternion = new THREE.Quaternion;
const sphere = new THREE.Mesh(
new THREE.SphereGeometry( 10, 10, 10 ),
new THREE.MeshBasicMaterial({ transparent: true, opacity: .1 })
);
const arrow = new THREE.Group;
const arrowShaft = new THREE.Mesh(
// We want to ensure our arrow is completely offset into one direction
// So the translation ensure every bit of it is in Y+
new THREE.CylinderGeometry( .1, .3, 3 ).translate( 0, 1.5, 0 ),
new THREE.MeshBasicMaterial({ color: 'blue' })
);
const arrowPoint = new THREE.Mesh(
// Same thing, translate to all vertices or +Y
new THREE.ConeGeometry( 1, 2, 10 ).translate( 0, 1, 0 ),
new THREE.MeshBasicMaterial({ color: 'red' })
);
const trackerPoint = new THREE.Mesh(
new THREE.SphereGeometry( .2 ),
new THREE.MeshBasicMaterial({ color: 'green' })
);
const clickerPoint = new THREE.Mesh(
trackerPoint.geometry,
new THREE.MeshBasicMaterial({ color: 'yellow' })
);
camera.position.set( 10, 10, 10 );
camera.lookAt( scene.position );
// Place the point at the top of the shaft
arrowPoint.position.y = 3;
// Point the shaft into the z-direction
arrowShaft.rotation.x = Math.PI / 2;
// Attach the point to the shaft
arrowShaft.add( arrowPoint );
// Add the shaft to the global arrow group
arrow.add( arrowShaft );
// Add the arrow to the scene
scene.add( arrow );
scene.add( sphere );
scene.add( trackerPoint );
scene.add( clickerPoint );
renderer.domElement.addEventListener( 'mousemove', mouseMove );
renderer.domElement.addEventListener( 'click', mouseClick );
renderer.domElement.addEventListener( 'wheel', mouseWheel );
render();
document.body.appendChild( renderer.domElement );
function render(){
renderer.setSize( innerWidth, innerHeight );
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.render( scene, camera );
}
function mouseMove( event ){
mouse.set(
event.clientX / event.target.clientWidth * 2 - 1,
-event.clientY / event.target.clientHeight * 2 + 1
);
raycaster.setFromCamera( mouse, camera );
const hit = raycaster.intersectObject( sphere ).shift();
if( hit ){
trackerPoint.position.copy( hit.point );
render();
}
document.body.classList.toggle( 'tracking', !!hit );
}
function mouseClick( event ){
clickerPoint.position.copy( trackerPoint.position );
arrow.lookAt( trackerPoint.position );
render();
}
function mouseWheel( event ){
const angle = Math.PI * event.wheelDeltaX / innerWidth;
camera.position.applyQuaternion(
quaternion.setFromAxisAngle( scene.up, angle )
);
camera.lookAt( scene.position );
render();
}
body { padding: 0; margin: 0; }
body.tracking { cursor: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r123/three.min.js"></script>
You can wheel around using your mouse (if it has horizontal scroll, should be on trackpads) and click to point the arrow. I also added some tracking points so you can see that `lookAt' does work without overcomplicating it, and that is is pointing at the point you clicked on the wrapping sphere.
And with that, I definitely typed the word shaft too often. It's starting to sound weird.

Three JS Keep Label Size On Zoom

I'm working on a solar system in three.js and am curious if there is an easy way to make the labels for the planets I have below all show up the same size regardless of how far they are from the camera? I can't seem to find a solution to this. I figure you could calculate the distance from each label to the camera then come up with some sort of scaling factor based on that. Seems like there would be an easier way to accomplish this?
Thanks!
Updated with answer from prisoner849. Works excellent!
I figure you could calculate the distance from each label to the camera then come up with some sort of scaling factor based on that.
And it's very simple. Let's say, a THREE.Sprite() object (label) is a child of a THREE.Mesh() object (planet), then in your animation loop you need to do
var scaleVector = new THREE.Vector3();
var scaleFactor = 4;
var sprite = planet.children[0];
var scale = scaleVector.subVectors(planet.position, camera.position).length() / scaleFactor;
sprite.scale.set(scale, scale, 1);
I've made a very simple example of the Solar System, using this technique.
For the benefit of future visitors, the transform controls example does exactly this:
https://threejs.org/examples/misc_controls_transform.html
Here's how its done in the example code:
var factor;
if ( this.camera.isOrthographicCamera ) {
factor = ( this.camera.top - this.camera.bottom ) / this.camera.zoom;
} else {
factor = this.worldPosition.distanceTo( this.cameraPosition ) * Math.min( 1.9 * Math.tan( Math.PI * this.camera.fov / 360 ) / this.camera.zoom, 7 );
}
handle.scale.set( 1, 1, 1 ).multiplyScalar( factor * this.size / 7 );
Finally I found the answer to your question:
First, create a DOM Element:
<div class="element">Not Earth</div>
Then set CSS styles for it:
.element {position: absolute; top:0; left:0; color: white}
// |-------------------------------| |-----------|
// make the element on top of canvas is
// the canvas black, so text
// must be white
After that, create moveDom() function and run it every time you render the scene requestAnimationFrame()
geometry is the geometry of the mesh
cube is the mesh you want to create label
var moveDom = function(){
vector = geometry.vertices[0].clone();
vector.applyMatrix4(cube.matrix);
vector.project(camera);
vector.x = (vector.x * innerWidth/2) + innerWidth/2;
vector.y = -(vector.y * innerHeight/2) + innerHeight/2;
//Get the DOM element and apply transforms on it
document.querySelectorAll(".element")[0].style.webkitTransform = "translate("+vector.x+"px,"+vector.y+"px)";
document.querySelectorAll(".element")[0].style.transform = "translate("+vector.x+"px,"+vector.y+"px)";
};
You can create a for loop to set label for all the mesh in your scene.
Because this trick only set 2D position of DOM Element, the size of label is the same even if you zoom (the label is not part of three.js scene).
Full test case: https://jsfiddle.net/0L1rpayz/1/
var renderer, scene, camera, cube, vector, geometry;
var ww = window.innerWidth,
wh = window.innerHeight;
function init(){
renderer = new THREE.WebGLRenderer({canvas : document.getElementById('scene')});
renderer.setSize(ww,wh);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50,ww/wh, 0.1, 10000 );
camera.position.set(0,0,500);
scene.add(camera);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set( 0, 0, 500 );
scene.add(light);
//Vector use to get position of vertice
vector = new THREE.Vector3();
//Generate Not Earth
geometry = new THREE.BoxGeometry(50,50,50);
var material = new THREE.MeshLambertMaterial({color: 0x00ff00});
cube = new THREE.Mesh(geometry, material);
scene.add(cube);
//Render my scene
render();
}
var moveDom = function(){
vector = geometry.vertices[0].clone();
vector.applyMatrix4(cube.matrix);
vector.project(camera);
vector.x = (vector.x * ww/2) + ww/2;
vector.y = -(vector.y * wh/2) + wh/2;
//Get the DOM element and apply transforms on it
document.querySelectorAll(".element")[0].style.webkitTransform = "translate("+vector.x+"px,"+vector.y+"px)";
document.querySelectorAll(".element")[0].style.transform = "translate("+vector.x+"px,"+vector.y+"px)";
};
var counter = 0;
var render = function (a) {
requestAnimationFrame(render);
counter++;
//Move my cubes
cube.position.x = Math.cos((counter+1*150)/200)*(ww/6+1*80);
cube.position.y = Math.sin((counter+1*150)/200)*(70+1*80);
cube.rotation.x += .001*1+.002;
cube.rotation.y += .001*1+.02;
//Move my dom elements
moveDom();
renderer.render(scene, camera);
};
init();
body,html, canvas{width:100%;height:100%;padding:0;margin:0;overflow: hidden;}
.element{color:white;position:absolute;top:0;left:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<!-- My scene -->
<canvas id="scene"></canvas>
<div class="element">
<h1>Not Earth</h1>
</div>
If you downvote this, please tell me why. I will try my best to improve my posts.
If you are using spriteMaterial to present your text, you could try to set the sizeAttenuation attribute to false.
var spriteMaterial = new THREE.SpriteMaterial( { map: spriteMap, color: 0xffffff, sizeAttenuation:false } );
See more information from here:
https://threejs.org/docs/index.html#api/en/materials/SpriteMaterial.sizeAttenuation

Why sphere geometry take more time to render than cube geometry while both of them have equal faces value?

I created a cube from BoxGeometry and created a sphere from SphereGeometry.
(those objects spin around itself)
When a number of both objects were increased, i found that sphere take more time to render than cube.
For example
500 cubes get 60 FPS
500 spheres get 45 FPS
while each object has the same 12 faces.
Why is it?
for(var i = 0; i <amount ; i++){
posX = posX + size;
if(i%20 == 0){
posX = -105;
posZ = posZ + size;
}
if(i%500 == 0){
posX = -105;
posZ = -500;
posY = posY + size;
} // To fit for view, five hundred objects were stacked per level.
sphereGeo = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
sphereMat = new THREE.MeshPhongMaterial({ color: 0x219621, specular: 0xFFE7BA, shininess: 5, opacity: 1, transparent: true});
sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
sphereMesh.rotation.x =0;
sphereMesh.rotation.y =0;
sphereMesh.rotation.z =0;
sphereMesh.position.x = posX;
sphereMesh.position.y = posY;
sphereMesh.position.z = posZ;
sphereMesh.matrixAutoUpdate = false;
sphereMesh.updateMatrix();
sphereMesh.address = i;
countID = i;
objectsID[countID] = sphereMesh;
addList[sphereMesh.address] = objectsID[countID];
scene.add(objectsID[countID]);
}
function render()
{
var add_len = addList.length;
for(var i=0; i< add_len; i++ )
{
addList[i].rotation.y += 1;
addList[i].matrixAutoUpdate = false;
addList[i].updateMatrix();
}
renderer.render( scene, camera );
requestAnimationFrame( render );
}
these are pictures for
500 spheres
500 cubes
i set radius = 25, WidthSegments = 3 and heightSegments = 2
. So it has 12 faces but the output unlike the spherical shape
(3 and 2 are a minimum value which define in three.js Docs for segment)
For cubes creating, i use the same method and each cube has 12 faces.
Both testing case are run on the same environment.
i wonder why they have a difference performance.
maybe cause from a structure inside BoxGeometry and SphereGeometry?
Are the faces for both objects always pointing in the same direction (e.g. did you create a '12 face cube' out of a sphere somehow)?
If not, then normals and lighting would be more varied on the non-cubed object since the cube would have more faces that could share the same data (less calculations).

Meshes are in Scene however nothing but the renderer's clear color appears

So I'm working with Three.js and jQuery to create a small visual application. At the moment all I want is for all the meshes I have, to appear on screen.
The Problem: None of the meshes appear on screen whatsoever.
Exceptions: The renderer's clear color appears (0x00bfff) and console.log(scene) confirms that all the meshes are in the scene.
Attempts to Fix: Use THREE.Projector, THREE.Raycaster, change camera positioning, and many more attempts.
I'm still very new to Three.js and programming in general so please be very critical of my work. Anything helps! Thanks!
WORLD.JS
$(document).ready(function() {
initialize();
animate();
});
var initialize = function() {
clock = new THREE.Clock(); // timer used to calculate time between rendering frames
scene = new THREE.Scene(); // list of objects that are to be "read" (rendered)
camera = new THREE.PerspectiveCamera(35, // FOV
window.innerWidth / window.innerHeight, // Aspect Ratio
.1, // Near
10000); // Far
camera.position.set( 25, 25, 125 );
camera.lookAt( scene.position );
setupEnvironment();
setupAI();
renderer = new THREE.WebGLRenderer(); // renderer will draw as WebGL rather than HTML5 Canvas
renderer.setSize( window.innerWidth, window.innerHeight ); // size of the canvas that renderer will draw on
renderer.setClearColor( 0x00bfff, 1 );
document.body.appendChild( renderer.domElement ); // adds the canvas to the document
};
var animate = function() { // animates the scene with frames
requestAnimationFrame(animate); // works recursively
render(); // update and display
}
var render = function() {
var delta = clock.getDelta() // gets the seconds passed since the last call to this method
// AI collision needed
// AI update needed
renderer.render( scene, camera ) // repaint
}
var setupEnvironment = function() {
ground = new BoxMesh( 10, 0.1, 10, 0x6C4319, 1 );
positionThenAdd( ground, [[ 0, 0 ]] );
light1 = new THREE.PointLight( 0xFFFFFF, .5 );
light1.position.set( 10, 10, 10 );
scene.add( light1 );
light2 = new THREE.PointLight( 0xFFFFFF, 1 );
light2.position.set( -10, -10, 10 );
scene.add( light2 );
};
var setupAI = function() {
sheep = new BoxMesh( 1, 1, 1, 0xFFFFFF, 3 );
positionThenAdd( sheep, [[ 0, 0 ],
[ 4.5, 0 ],
[ 9.5, 0 ]]);
sheepHerder = new BoxMesh( 1, 1, 1, 0x996633, 1 );
positionThenAdd( sheepHerder, [[ 4.5, 7.5 ]] );
};
function BoxMesh( width, height, depth, hexColor, amount ) { // creates one or more box meshes
this.width = width;
this.height = height;
this.depth = depth;
this.hexColor = hexColor;
this.amount = amount; // amount of box meshes to be made
boxSize = new THREE.BoxGeometry( width, height, depth );
boxMaterial = new THREE.MeshLambertMaterial( { color: hexColor } );
var all = []; // will contain all of the box meshes
for(var n = 1; n <= amount; n++) { // adds a new box mesh to the end of the all array
all.push(new THREE.Mesh( boxSize, boxMaterial )); // uses the attributes given by the BoxMesh constructor's parameters
}
return all; // returns all of the created box meshes as an array;
}
var positionThenAdd = function( varMesh, posArrXByZ ) { // positions an object and then adds it to the scene
this.varMesh = varMesh; // variable name of the mesh(es) array
this.posArrXByZ = posArrXByZ; // posArrXByZ stands for "array of positions in the format of X-by-Z"
// posArrXByZ is a 2 dimensional array where the first dimension is for the specific mesh to be positioned...
// and the second dimension is the positional coordinates.
// posArrXByZ = [ [x0,z0], [x1,z1], ...[xn,zn] ]
for(var mesh = 0; mesh < varMesh.length; mesh++) { // mesh accesses the varMesh array
varMesh[mesh].position.set( varMesh[mesh].geometry.parameters.width/2 + posArrXByZ[mesh][0], // the x coordinate, varMesh[mesh].width/2 makes the x coordinate act upon the closest side
varMesh[mesh].geometry.parameters.height/2 + ground.height, // the y coordinate, which is pre-set to rest on top of the ground
varMesh[mesh].geometry.parameters.depth/2 + posArrXByZ[mesh][1] ); // the z coordinate, varMesh[mesh].height/2 makes the y coordinate act upon the closest side
scene.add( varMesh[mesh] ); // adds the specific mesh that was just positioned
}
};
HTML FILE
<!DOCTYPE html>
<html>
<head>
<title>Taro's World</title>
<style>
body {
margin: 0;
padding: 0;
border: 0;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="mrdoob-three.js-d6384d2/build/Three.js"></script>
<script src="mrdoob-three.js-d6384d2/examples/js/renderers/Projector.js"></script>
<script src="world.js"></script>
</head>
<body></body>
</html>
Two things are broken in your script :
in your positionThenAdd function, at position.set(...), you wrote somewhere ground.height. ground is an array, you probably meant varMesh[mesh].geometry.parameters.height.
your console should print that positionThenAdd is not a function. While you declared previous functions writing function myFunction(){....} you declared this one that way : var positionThenAdd = function () { ... };. The difference in javascript is that, as any variable, positionThenAdd will then be reachable in the script order. Since you write it at the end, nothing can reach it. You just have to modify its declaration to function positionThenAdd(){...}. See var functionName = function() {} vs function functionName() {}
Your scene : http://jsfiddle.net/ba8vvkyg/1/

Interpolated surface in Three.js

Hi folks,
I've got a question belongig surfaces in Three.js:
I got a bunch of Vec3 Points and want want to interpolate a surface through them. While searching, I stumbeled across beziers (three.js bezier - only as lines) and what looked more like I was searching : three.js Nurbs. I've tried to reconstruct the code, but the documentation was terrible (pages like this) and I didn't get how everything worked by reconstructing the code...
So here's the question:
Is there any easy way to get a shape out of my calculated points? (I would still be happy, if it's not interpolated).
Thank you guys!
Mat
Edit: What I want to acchieve is a surface plot. I stumbeled across http://acko.net/blog/making-mathbox/ but it's way too big for my needs...
After some try and error I found a solution: add a plane and than transform the single vertices.
// need to setup 'step', 'xStart', 'xEnd', 'yStart', 'yEnd'
// calc the variables
var width = Math.abs(-xStart+xEnd),
height = Math.abs(-yStart+yEnd);
var stepsX = width*step, stepsY = height*step;
var posX = (xStart+xEnd)/2;
var posZ = (yStart+yEnd)/2;
// add a plane and morph it to a function
var geometry = new THREE.PlaneGeometry( width, height, stepsX - 1, stepsY - 1 );
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
var size = stepsX * (stepsY),
data = new Float32Array( size );
var count = 0, scope = {};
mesh = new THREE.Mesh( geometry, new THREE.MeshNormalMaterial( {
side : THREE.DoubleSide,
transparent: true,
shading: THREE.SmoothShading,
opacity : _opacity }));
mesh.updateMatrixWorld();
// calc y value for every vertice
for ( var i = 0; i < size; i ++ ) {
// calculate the current values
// http://stackoverflow.com/questions/11495089/how-to-get-the-absolute-position-of-a-vertex-in-three-js
var vector = mesh.geometry.vertices[i].clone();
vector.applyMatrix4(
mesh.matrixWorld
);
// set them into the scope
scope.x = vector.x + posX;
scope.y = vector.z + posZ;
// calculate point and write it in a temp array
data[i] = math.eval(term, scope);
}
// push the new vertice data
for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
geometry.vertices[ i ].y = data[ i ];
}
// update the new normals
geometry.computeFaceNormals();
geometry.computeVertexNormals();
// add to scene
scene.add( mesh );
Only issue is that it is not working for non static functions like tan(x). This snippet is using math.js to calc the term.
Greetings Mat

Categories