How can I scale down a mesh in three.js - javascript

I have a mesh that I want to scale down (shrink) in three.js.
I've tried the following code with no hope:
Can anyone advise please?
drawMesh(vAngles, hAngles, intensities) {
let vLines = this.getVerticalDistribution(vAngles, hAngles, intensities);
let hLines = this.getHorizontalDistribution(vAngles, hAngles, intensities);
let mesh = new THREE.Mesh();
mesh.add(...vLines);
mesh.add(...hLines);
mesh.rotation.y = Math.PI;
mesh.scale.set(0.25,0.25, 0.25);
mesh.matrixAutoUpdate = true;
}

When I set the scale value to 0.05 the Mesh get scaled down, so I think the units used needed to match the scale ratio.

Related

THREE.js translation/position weirdness

I'm trying to generate random planes that would be in random position in the screen and of random size. The problem I have is that I really don't understand the set.position values. What are they representing? I tried to search every where for an answer but couldn't find.
Here's my code for the planes:
var planes = [];
for (i = 0; i < 20; i++) {
var geometry = new THREE.PlaneGeometry(sizeWidth*((randomSO(1,20)*0.00001)), sizeHeight*((randomSO(1,10)*0.0001)));
var material = new THREE.MeshBasicMaterial( {color: 0x00ff00});
var plane = new THREE.Mesh(geometry, material);
plane.drawMode = THREE.TrianglesDrawMode;
plane.position.set(-10+i, 0, 0);
scene.add(plane);
planes[i] = plane;
}
The X-co-ordinate in above example isn't how I'd want it but is like that for testing. I would like to work according to screensize, that each plane would locate in random % of the width and height. The width of my screen is 1280 and now when I set the the x co-ordinate to let's say -20 it's already out of the screen. Should I set the Z co-ordinate of the camera differently? I really don't understand the position co-ordinates in this :-(

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

Dynamically add and rotate a geometry in three.js

Refer https://jsfiddle.net/pmankar/svt0nhuv/
Main large red icosahedron geometry keeps rotating about the y axis. I added a small red sphere geometry and merged it to the main large red icosahedron geometry. Until here it works fine. For this I used, THREE.GeometryUtils.merge(point_sphere_iso_geom, sphere);
However, when I try to add spheres dynamically with a mouse click, they are added (yellow spheres), but they do not rotate with the main large red icosahedron geometry.
Can anyone explain why does it works in the initial case, but not when added dynamically and how to achieve it dynamically as well.
I hope I understood you correctly. Every mouse click you have to create a new geometry based on the previous one (mesh geometry and mesh matrix), merging it with the geometry of a new sphere, and apply it to a new mesh, then remove the old mesh and add the new one.
some changes in vars
var geometry, material, point_sphere_iso_geom, mesh;
in creation of the start merged mesh
point_sphere_iso_geom = new THREE.IcosahedronGeometry(100, 4);
cygeo = new THREE.SphereGeometry(5, 10, 10);
cygeo.translate(0,0,120);
point_sphere_iso_geom.merge( cygeo );
mesh = new THREE.Mesh(point_sphere_iso_geom, material);
and in the function of addYellowPoint
function addYellowPoint(valX, valY) {
var sgeometry = new THREE.SphereGeometry(2.5, 10, 10);
var range = 150;
var x = Math.random() * range - range / 2;
var y = Math.random() * range - range / 2;
var z = Math.random() * range - range / 2;
sgeometry.translate(x,y,z);
point_sphere_iso_geom = mesh.geometry.clone();
point_sphere_iso_geom.applyMatrix(mesh.matrix);
point_sphere_iso_geom.merge(sgeometry);
scene.remove(mesh);
mesh.geometry.dispose();
mesh.material.dispose();
mesh = new THREE.Mesh(point_sphere_iso_geom, material);
scene.add(mesh);
}

How would I create a terrain like this in Three.js

I am trying to create a terrain from a heightmap with a "closed" bottom see the example here:
http://demos.playfuljs.com/terrain/
My terrain generation function is as so:
var img = document.getElementById("landscape-image");
var numSegments = img.width - 1; // We have one less vertex than pixel
var geometry = new THREE.PlaneGeometry(2400, 2400, numSegments, numSegments);
var material = new THREE.MeshLambertMaterial({
color: 0xccccff,
wireframe: false
});
plane = new THREE.Mesh(geometry, material);
plane.name = 'Terrain';
// set height of vertices
for (var i = 0; i < plane.geometry.vertices.length; i++) {
plane.geometry.vertices[i].z = (terrain[i]/255) * height;
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
plane.position.x = 0;
plane.rotation.x = 0;
plane.position.y = -10;
The problem I am having is how do I create that connected bottom part of the terrain with a THREE.PlaneGeometry. I can't extrude as:
The bottom must be flat if I extrude it will be bumpy like the
terrain.
Extrude takes a Shape object, not a Geometry object.
Really scratching my head here anyone done this before.
Edit: Maybe I could use two planes and merge them but how would I merge the side faces into a single piece of Geometery ?
P.S. The example draws straight to the canvas
create a plane for each side which has your amount of Segments in width and 1 in height. them set the top Vertices according to your heightmap
something like this
var geometry_l = new THREE.PlaneGeometry(2400, 0, numSegments, 1);
plane_l = new THREE.Mesh(geometry_l, material);
for (var i = 0; i < numSegments; i++) {
plane_l.geometry_l.vertices[i].z = (Terrain[0][i]/255) * height;
}
//Offset to the edge of your main plane
you might want to Change your Terrain Array to be two-dimensional for this. so you can always read the row you want.

Three.js point light not working with large meshes

I have the following problem. When I use Three.js point light like this:
var color = 0xffffff;
var intensity = 0.5;
var distance = 200;
position_x = 0;
position_y = 0;
position_z = 0;
light = new THREE.PointLight(color, intensity, distance);
light.position.set(position_x, position_y, position_z);
scene.add(light);
It works as expected when there is a "small" object (mesh) positioned close to the light on the scene. However, when there is a large object (let us say a floor):
var floorTexture = new THREE.ImageUtils.loadTexture( 'floor.jpg' );
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set( 1, 1);
var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.y = -0.5;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
Then the light will not be shown on it. At first I thought it is due to the fact that the floor center is positioned further away from the point light so the point light cannot reach it with the distance set to 200 (even though part of the floor is closer than the mentioned distance). Therefore I have tryied to increase this distance - no luck.
There is a workaround to create a floor out of small parts. Then the point light again works as expected but there is a problem with this approach - namely it drastically decreases FPS due to the large number of "floor objects" to be rendered.
My guess is that I am missing something. I know that there are other types of light which cover the whole scene but I am trying to create a lamp, so I think I need to use a point light. But I might be wrong. Any help or hint how to make this work would be appreciated.
MeshBasicMaterial does not support lights. Use MeshPhongMaterial.
MeshLambertMaterial also supports lights, but it is not advisable in your case for reasons explained here: Three.js: What Is The Exact Difference Between Lambert and Phong?.
three.js r.66

Categories