Can't point one position at another in three.js - javascript

I may be misunderstanding the way positions work, but they're not working the way I would expect. If I create two Object3Ds I would expect to be able to give the position property of the second a reference to the first so that it follows it, but that doesn't seem to work:
var foo = new THREE.Object3D();
foo.position = new THREE.Vector3(100, 200, 300);
var bar= new THREE.Object3D();
bar.position = foo.position;
console.log(foo.position); //{100, 200, 300}
console.log(bar.position); //{0, 0, 0}
What am I doing wrong here? There is another way that seems to create a new Vector3:
var foo = new THREE.Object3D();
foo.position = new THREE.Vector3(100, 200, 300);
var bar= new THREE.Object3D();
bar.position = foo.position.copy();
console.log(foo.position); //{100, 200, 300}
console.log(bar.position); //{100, 200, 300}
As this creates a new Vector3, this works only until I move foo - then I have to update bar.position again.
(above is untested code!)

bar.position.set(foo.position.x,foo.position.y,foo.position.z);
Always use class .set(x,y,z), don't use position = new.THREE.Vector3(1,2,3)

If you want one object to follow e.g. attach it to another then you would need to .add() the following object to the one being followed.
See this fiddle http://jsfiddle.net/cdc1u9dk/ where you can click on the cube and the cube jumps a bit to the left and the sphere 'follows' the cube since it was attached to it in line 19 cube.add( sphere );

Related

ThreeJS: Water2 example throwing "sphere is undefined," error

I'm trying to get the basic ThreeJS Water2 example setup that you can find here: https://threejs.org/examples/?q=water#webgl_water
Source code here: https://github.com/mrdoob/three.js/blob/master/examples/webgl_water.html
The process seems so straightforward it hurts that it's not working: Supply the geometry of a plane to the constructor, supply parameters, and then add that object to the scene. You can see an example of this on line 86 of the examples' source above.
Here's my attempt at doing it inside an Aframe component:
init() {
let mesh;
let waterObj;
this.el.object3D.traverse(obj => {
if (obj.type == "Mesh") {
mesh = obj;
}
});
waterObj = new Water(mesh, {
scale: 4,
flowDirection: new THREE.Vector2(1, 1),
textureWidth: 1024,
textureHeight: 1024
});
this.el.object3D.attach(waterObj);
}
});
but it doesn't seem to work. If I use object3D.attach() it produces an error that says sphere is undefined (no idea what "sphere," is), and if I use object3D = waterObj then the color of the plane slightly changes, but nothing else.
Does anyone have experience with getting this setup?
It should be working as long as you provide the geometry in the constructor, not the mesh:
waterObj = new Water(mesh.geometry, {
scale: 4,
flowDirection: new THREE.Vector2(1, 1),
textureWidth: 1024,
textureHeight: 1024
});
Also keep in mind that attach won't apply the parent transform to the waterObj, so the plane might appear at (0,0,0).
Other than that - it should be working - check out
this a-frame + THREE.Water example

Extract arguments from functions & use them as logical sentences

I was exploring THREE.js & was trying different geometries in it. So as it has a whole lot of geometries writing every single one manually is too boring.
I would have to write these lines for a simple cube to be displayed:
var material = new THREE.MeshNormalMaterial();
var boxGeometry = new THREE.BoxGeometry( 20, 20, 20 );
var box = new THREE.Mesh( boxGeometry, material );
box.position.set(-10, -10, 0);
scene.add( box );
To add another one say a cone i would have to just copy paste the above lines & then replace Box with Cone & you know that's my computer's work (not mine). Humans are not made for copying :)
So i wanted a general class after which i can say something like:
var cube = new Shape('Cube', 20, 20, 20);
// or
var cone = new Shape('Cone', 20, 30);
& that may do everything else for me, i can extract the arguments from the function but then what to do?
Say how to convert them from strings to logical statements?
With bracket notation, you can make a function that accesses Three.<word>Geometry, while using rest syntax to collect the other arguments, and then spread them into the constructor:
function makeShape(shapeName, ...args) {
var material = new THREE.MeshNormalMaterial();
var boxGeometry = new THREE[shapeName + 'Geometry'](...args);
var box = new THREE.Mesh( boxGeometry, material );
box.position.set(-10, -10, 0);
scene.add( box );
}
Then, just call that function:
makeShape('Cube', 20, 20, 20);
makeShape('Cone', 20, 30);
If you want to assign something created in the function to the caller of makeShape, just return it at the end.

three js spotlight above r73

I have a problem with spotlight. I was using r.73 and had 50x simple Spotlights without shadows etc.. it works without problems, still 60fps also on mobile.
Now i was changed to r84 (The problem occurs above r73), and the spotlights are much better quality but also drop my frames. I know there was some changes with adding penumbra options in r74.. i not really understand how can i set down the quality..
On fiddle , you dont see qualityChanges, dont matter. but Frames will drope.
So my Question, is it possible to set up the spotlight of a way, i still have 60frames?
The mistake occurs only when the mesh (floor) is big enough.
var spotLightSize=50;
var spotLight=[];
var geometry = new THREE.BoxGeometry( 500, 1, 500 );
var material = new THREE.MeshPhongMaterial( {color: "blue"} );
var floor = new THREE.Mesh( geometry, material );
var renderer = new THREE.WebGLRenderer({precision:"lowp",alpha:true});
for (var i=0;i<spotLightSize;i++){
spotLight.push(new THREE.SpotLight("green" ,2,20,0.1,0,1));
spotLight[spotLight.length-1].position.set( 0, 5, 0 );
scene.add(spotLight[spotLight.length-1]);
var spotLightHelper = new THREE.SpotLightHelper( spotLight[spotLight.length-1] );
scene.add( spotLightHelper );
}
http://jsfiddle.net/killerkarnikel/hyqgjLLz/19/

How to merge two geometries or meshes using three.js r71?

Here I bumped to the problem since I need to merge two geometries (or meshes) to one. Using the earlier versions of three.js there was a nice function:
THREE.GeometryUtils.merge(pendulum, ball);
However, it is not on the new version anymore.
I tried to merge pendulum and ball with the following code:
ball is a mesh.
var ballGeo = new THREE.SphereGeometry(24,35,35);
var ballMat = new THREE.MeshPhongMaterial({color: 0xF7FE2E});
var ball = new THREE.Mesh(ballGeo, ballMat);
ball.position.set(0,0,0);
var pendulum = new THREE.CylinderGeometry(1, 1, 20, 16);
ball.updateMatrix();
pendulum.merge(ball.geometry, ball.matrix);
scene.add(pendulum);
After all, I got the following error:
THREE.Object3D.add: object not an instance of THREE.Object3D. THREE.CylinderGeometry {uuid: "688B0EB1-70F7-4C51-86DB-5B1B90A8A24C", name: "", type: "CylinderGeometry", vertices: Array[1332], colors: Array[0]…}THREE.error # three_r71.js:35THREE.Object3D.add # three_r71.js:7770(anonymous function) # pendulum.js:20
To explain Darius' answer more clearly (as I struggled with it, while trying to update a version of Mr Doob's procedural city to work with the Face3 boxes):
Essentially you are merging all of your Meshes into a single Geometry. So, if you, for instance, want to merge a box and sphere:
var box = new THREE.BoxGeometry(1, 1, 1);
var sphere = new THREE.SphereGeometry(.65, 32, 32);
...into a single geometry:
var singleGeometry = new THREE.Geometry();
...you would create a Mesh for each geometry:
var boxMesh = new THREE.Mesh(box);
var sphereMesh = new THREE.Mesh(sphere);
...then call the merge method of the single geometry for each, passing the geometry and matrix of each into the method:
boxMesh.updateMatrix(); // as needed
singleGeometry.merge(boxMesh.geometry, boxMesh.matrix);
sphereMesh.updateMatrix(); // as needed
singleGeometry.merge(sphereMesh.geometry, sphereMesh.matrix);
Once merged, create a mesh from the single geometry and add to the scene:
var material = new THREE.MeshPhongMaterial({color: 0xFF0000});
var mesh = new THREE.Mesh(singleGeometry, material);
scene.add(mesh);
A working example:
<!DOCTYPE html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.js"></script>
<!-- OrbitControls.js is not versioned and may stop working with r77 -->
<script src='http://threejs.org/examples/js/controls/OrbitControls.js'></script>
<body style='margin: 0px; background-color: #bbbbbb; overflow: hidden;'>
<script>
// init renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// init scene and camera
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 3000);
camera.position.z = 5;
var controls = new THREE.OrbitControls(camera)
// our code
var box = new THREE.BoxGeometry(1, 1, 1);
var sphere = new THREE.SphereGeometry(.65, 32, 32);
var singleGeometry = new THREE.Geometry();
var boxMesh = new THREE.Mesh(box);
var sphereMesh = new THREE.Mesh(sphere);
boxMesh.updateMatrix(); // as needed
singleGeometry.merge(boxMesh.geometry, boxMesh.matrix);
sphereMesh.updateMatrix(); // as needed
singleGeometry.merge(sphereMesh.geometry, sphereMesh.matrix);
var material = new THREE.MeshPhongMaterial({color: 0xFF0000});
var mesh = new THREE.Mesh(singleGeometry, material);
scene.add(mesh);
// a light
var light = new THREE.HemisphereLight(0xfffff0, 0x101020, 1.25);
light.position.set(0.75, 1, 0.25);
scene.add(light);
// render
requestAnimationFrame(function animate(){
requestAnimationFrame(animate);
renderer.render(scene, camera);
})
</script>
</body>
At least, that's how I am interpreting things; apologies to anyone if I have something wrong, as I am no where close to being a three.js expert (currently learning). I just had the "bad luck" to try my hand at customizing Mr. Doob's procedural city code, when the latest version breaks things (the merge stuff being one of them, the fact that three.js no longer uses quads for cube -ahem- box geometry the other - which has led to all kinds of fun getting the shading and such to work properly again).
Finally, I found a possible solution. I am posting since it could be useful for somebody else while I wasted a lot of hours. The tricky thing is about manipulating the concept of meshes and geometries:
var ballGeo = new THREE.SphereGeometry(10,35,35);
var material = new THREE.MeshPhongMaterial({color: 0xF7FE2E});
var ball = new THREE.Mesh(ballGeo, material);
var pendulumGeo = new THREE.CylinderGeometry(1, 1, 50, 16);
ball.updateMatrix();
pendulumGeo.merge(ball.geometry, ball.matrix);
var pendulum = new THREE.Mesh(pendulumGeo, material);
scene.add(pendulum);
The error message is right. CylinderGeometry is not an Object3D. Mesh is. A Mesh is constructed from a Geometry and a Material. A Mesh can be added to the scene, while a Geometry cannot.
In the newest versions of three.js, Geometry has two merge methods: merge and mergeMesh.
merge takes a mandatory argument geometry, and two optional arguments matrix and materialIndexOffset.
geom.mergeMesh(mesh) is basically a shorthand for geom.merge(mesh.geometry, mesh.matrix), as used in other answers. ('geom' and 'mesh' being arbitrary names for a Geometry and a Mesh, respectively.) The Material of the Mesh is ignored.
This is my ultimate compact version in four (or five) lines (as long as material is defined somewhere else) making use of mergeMesh:
var geom = new THREE.Geometry();
geom.mergeMesh(new THREE.Mesh(new THREE.BoxGeometry(2,20,2)));
geom.mergeMesh(new THREE.Mesh(new THREE.BoxGeometry(5,5,5)));
geom.mergeVertices(); // optional
scene.add(new THREE.Mesh(geom, material));
Edit: added optional extra line to remove duplicate vertices, which should help performance.
Edit 2: I'm using the newest version, 94.
The answers and code that I've seen posted here do not work because the second argument of the merge method is an integer, not a matrix. As far as I can tell, the merge method is not really functioning in a useful way. Therefore, I used the following approach to make a simple rocket with a nose cone.
import * as BufferGeometryUtils from '../three.js/examples/jsm/utils/BufferGeometryUtils.js'
lengthSegments = 2
radius = 5
radialSegments = 32
const bodyLength = dParamWithUnits['launchVehicleBodyLength'].value
const noseConeLength = dParamWithUnits['launchVehicleNoseConeLength'].value
// Create the vehicle's body
const launchVehicleBodyGeometry = new THREE.CylinderGeometry(radius, radius, bodyLength, radialSegments, lengthSegments, false)
launchVehicleBodyGeometry.name = "body"
// Create the nose cone
const launchVehicleNoseConeGeometry = new THREE.CylinderGeometry(0, radius, noseConeLength, radialSegments, lengthSegments, false)
launchVehicleNoseConeGeometry.name = "noseCone"
launchVehicleNoseConeGeometry.translate(0, (bodyLength+noseConeLength)/2, 0)
// Merge the nosecone into the body
const launchVehicleGeometry = BufferGeometryUtils.mergeBufferGeometries([launchVehicleBodyGeometry, launchVehicleNoseConeGeometry])
// Rotate the vehicle to horizontal
launchVehicleGeometry.rotateX(-Math.PI/2)
const launchVehicleMaterial = new THREE.MeshPhongMaterial( {color: 0x7f3f00})
const launchVehicleMesh = new THREE.Mesh(launchVehicleGeometry, launchVehicleMaterial)

Error 1105: Target of Assignment must be a Reference Value

Flash says the code is on my second line, but I've done some reading and it says this error is mainly due to me trying to assign a value to a value, and I've looked back over my code, and I can't seem to find any instance of this.
Here is my code:
this.mc=new MovieClip();
addChild(mc)=("myButton1", this.DisplayOBjectContainer.numChildren());
myButton1.createEmptyMovieClip("buttonBkg", myButton1.getNextHighestDepth());
myButton1.buttonBkg.lineStyle(0, 0x820F26, 60, true, "none", "square", "round");
myButton1.buttonBkg.lineTo(120, 0);
myButton1.buttonBkg.lineTo(120, 30);
myButton1.buttonBkg.lineTo(0, 30);
myButton1.buttonBkg.lineTo(0, 0);
Thanks!
Check what you are trying to in these lines:
addChild(mc)=("myButton1", this.DisplayOBjectContainer.numChildren());
myButton1.createEmptyMovieClip("buttonBkg", myButton1.getNextHighestDepth());
To create an empty MovieClip, you can just create a new display object w/o linking it to a clip in the library like this:
addChild(mc);
var myButton1:MovieClip = new MovieClip(); // creates an empty MovieClip
addChildAt(myButton1, numChildren); // adds the MovieClip at a defined level (on this case, the numChildren added on stage
Your code is very confusing. You mix AS2 methods (createEmptyMovieClip, getNextHighestDepth) with AS3 methods (addChild). Here is what you are trying to do:
const MC:Sprite = new Sprite(); // Sprite container
this.addChild(MC);
const MYBUTTON1:Sprite = new Sprite(); // button in container
MC.addChild(MYBUTTON1);
const BUTTONBKG:Shape = new Shape(); // background in button
MYBUTTON1.addChild(BUTTONBKG);
const BTG:* = BUTTONBKG.graphics;
BTG.beginFill(0x86B1FB);
BTG.lineStyle(0, 0x820F26, 0.6, true, "none", "square", "round");
BTG.lineTo(120, 0);
BTG.lineTo(120, 30);
BTG.lineTo(0, 30);
BTG.lineTo(0, 0);
MYBUTTON1.addEventListener(MouseEvent.CLICK, pressButton);
function pressButton(e:MouseEvent):void {
trace('I click on you');
}
Notes : the alpha value in AS3 is between 0 and 1. If you want your button to be clickable, you should use beginfill method.

Categories