Three.js render white part of plain geometry - javascript

I'm trying to turn this plainGeometry, into this (sort of mask).
This code works: (to sum-up, create two materials, divide the plane to segments and decide for each one which material is it with MeshFaceMaterial)
Button.onClick(function () {
var obj = editor.selected;
var material = obj.material;
var tex = material.map;
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, objWidth, objHeight);
var facesNum = objHeight * objWidth * 2;
var facesX = objWidth * 2;
var facesInX = texWidth * 2;
var materials = [];
materials.push(material);
materials.push(new THREE.MeshBasicMaterial({ }));
for (var i = 0; i < facesNum; i++) {
if ((i % facesX >= objWidth - texWidth) &&
(i % facesX <= (facesInX + objWidth - texWidth - 1)) &&
(i <= (texHeight * objWidth * 2) - 1)) {
geometry.faces[i].materialIndex = 0;
}
else {
geometry.faces[i].materialIndex = 1;
}
}
obj.geometry = geometry;
obj.material = new THREE.MeshFaceMaterial(materials);
editor.signals.materialChanged.dispatch(obj);
});
But I'm wondering if there is a simpler way to go. Any suggestions?

Another way to do this is to use an alpha channel on your texture. You can do this with Gimp or Photoshop.
You then duplicate the mesh and push it just a tad out the axis with polygonOffsetFactor on the material. Apply the background material to the first mesh, and the foreground material with the texture with alpha to the second.
See this fiddle alphaTest. (you may need to disable cross-domain access security so the texture can load in this fiddle, chrome will allow this if you run it with the --disable-web-security flag)
The advantage to this method is that the image can be of any shape and location, and doesn't need to fit into a geometry face.
Another way, if the geometry you were using is complicated, is to use Three.DecalGeometry to cut a mesh piece out and use it shifted a bit with polygonOffsetFactor on the material. See the three.js decals example for this.
Source for the example fiddle is below:
var renderer;
var pointLight;
var scene;
var plane1;
var plane2;
function addPlane()
{
var material1 = new THREE.MeshPhongMaterial({ color: 0xFFFFFF });
var material2;
var loader = new THREE.TextureLoader();
loader.load('http://i.imgur.com/ETdl4De.png',
function ( texture ) {
var material2 = new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
map: texture,
alphaTest: 0.7,
polygonOffset: true,
polygonOffsetFactor: - 4,
});
var geometry1 = new THREE.PlaneGeometry(12, 12, 12, 12);
var geometry2 = geometry1.clone();
plane1 = new THREE.Mesh(geometry1,material1);
plane2 = new THREE.Mesh(geometry2,material2);
scene.add(plane2);
scene.add(plane1);
}
);
}
(function() {
'use strict';
scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10000);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
addPlane();
camera.position.z = 52;
pointLight = new THREE.DirectionalLight(0xffffff,1);
pointLight.position.x = 11;
pointLight.position.y = 5;
pointLight.position.z = 25;
scene.add(pointLight);
var reqAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
var render = function() {
reqAnimFrame(render);
renderer.render(scene, camera);
};
render();
}());

this did it, eventually:
var obj = editor.selected;
var tex = obj.material.map.;
var materials = [];
materials.push(new THREE.MeshBasicMaterial({ map: tex }));
materials.push(new THREE.MeshBasicMaterial({}));
var material = new THREE.MeshFaceMaterial(materials);
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
tex.repeat = new THREE.Vector2(3, 3);
tex.offset = new THREE.Vector2(0, 0);
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, 3, 3);
var v = geometry.vertices;
var facesNum = geometry.faces.length;
v[1] = new THREE.Vector3(-texWidth / 2, objHeight / 2, 0);
v[2] = new THREE.Vector3(texWidth / 2, objHeight / 2, 0);
v[5] = new THREE.Vector3(-texWidth / 2, (objHeight / 2) - texHeight, 0);
v[6] = new THREE.Vector3(texWidth / 2, (objHeight / 2) - texHeight, 0);
v[9] = v[13];
v[10] = v[14];
v[4] = v[8] = v[12];
v[7] = v[11] = v[15];
for (var i = 0; i < facesNum; i++) {
if (i !== 2 && i !== 3) geometry.faces[i].materialIndex = 1;
}
obj.material = material;
obj.geometry = geometry;
editor.signals.materialChanged.dispatch(obj);

Related

Is there a way to align a geo json file on top of a terrain map perfectly? React, D3 and Three js

I have a Europe terrain map made from some DEM files which were then combined into one binary file. Then this file is read in React and the map is created. This can also be created using a simple heightmap but for the sake of this application I have to use the binary file.
Now when I have a terrain map I would like to add counties' borders using a geojson file to create geometries. The componentDidMount function is shown in the snippet below:
const SIZE_AMPLIFIER = 20;
const WIDTH = 2500 * SIZE_AMPLIFIER;
var container = document.getElementById("main_map");
var renderer, scene, camera;
var terrainData;
//load map datd from bin file
function loadTerrain(file) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', file, true);
xhr.onload = function (evt) {
if (xhr.response) {
terrainData = new Uint16Array(xhr.response)
init();
}
};
xhr.send(null);
}
loadTerrain('stats.bin');
function init() {
// initialize camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, .1, 100000);
camera.position.set(9191, 15000, 21000);
// initialize scene
scene = new THREE.Scene();
var containerWidth = container.getBoundingClientRect().right - container.getBoundingClientRect().left;
// initialize directional light (sun)
var sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
sun.position.set(300, 400, 300);
sun.distance = 1000;
scene.add(sun);
var frame = new THREE.SpotLightHelper(sun);
scene.add(frame);
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x006994);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(containerWidth, containerWidth / 2 + 150);
container.append(renderer.domElement);
//initialize controls
var controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 1;
controls.rotateSpeed = .8;
//controls.maxPolarAngle = Math.PI / 2 - .3;
//initialize plane ***** 999, 999 because file size is 1000x1000
var terrainGeometry = new THREE.PlaneBufferGeometry(WIDTH, WIDTH, 999, 999);
terrainGeometry.castShadow = true;
terrainGeometry.receiveShadow = true;
var heightsArray = terrainGeometry.attributes.position.array;
// apply height map to vertices of terrainGeometry
for (let i = 0, j = 2; i < terrainData.length; i += 1, j += 3) {
if (terrainData[i] == 0) {
heightsArray[j] = terrainData[i]
} else {
heightsArray[j] = terrainData[i] / 65535 * 480 + 10
}
}
var colorsArray = new Float32Array(heightsArray.length);
var waterLevel = 1.7;
var adjustHeight = 10 + waterLevel// 0.1 ~ 50cm water level, starts from 1.7
function addColors(counterJ, colorR, colorG, colorB) {
colorsArray[counterJ] = new THREE.Color(colorR).r;
colorsArray[counterJ + 1] = new THREE.Color(colorG).g;
colorsArray[counterJ + 2] = new THREE.Color(colorB).b;
}
for (let i = 2, j = 0; i < heightsArray.length; i += 3, j += 3) {
if (heightsArray[i] >= 0 && heightsArray[i] < 350 / adjustHeight) {
addColors(j, 0x000000, 0x006900, 0x000094);
}
else if (heightsArray[i] >= 350 / adjustHeight && heightsArray[i] < 900 / adjustHeight) {
addColors(j, 0x6e0000, 0x00dc00, 0x00006e);
}
else if (heightsArray[i] >= 900 / adjustHeight && heightsArray[i] < 1300 / adjustHeight) {
addColors(j, 0xf00000, 0x00fa00, 0x0000a0);
}
else if (heightsArray[i] >= 1300 / adjustHeight && heightsArray[i] < 1900 / adjustHeight) {
addColors(j, 0xe00000, 0x0bd00, 0x000077);
}
else if (heightsArray[i] >= 1900 / adjustHeight && heightsArray[i] < 2500 / adjustHeight) {
addColors(j, 0xdd0000, 0x009800, 0x000056);
}
else if (heightsArray[i] >= 2500 / adjustHeight && heightsArray[i] < 3300 / adjustHeight) {
addColors(j, 0xa00000, 0x005200, 0x00002d);
}
else {
addColors(j, 0xd20000, 0x00d200, 0x0000d2);
}
}
terrainGeometry.setAttribute('position', new THREE.BufferAttribute(heightsArray, 3));
terrainGeometry.setAttribute('color', new THREE.BufferAttribute(colorsArray, 3))
var terrainMaterial = new THREE.MeshLambertMaterial({
vertexColors: THREE.VertexColors, side: THREE.DoubleSide
})
var terrainMesh = new THREE.Mesh(terrainGeometry, terrainMaterial);
terrainMesh.rotation.x = - Math.PI / 2;
terrainMesh.matrixAutoUpdate = false;
terrainMesh.updateMatrix();
terrainGeometry.computeFaceNormals();
terrainGeometry.computeVertexNormals();
scene.add(terrainMesh);
const width1 = terrainGeometry.parameters.width; //2500 * SIZE_AMPLIFIER,
const projection = d3.geoTransverseMercator().rotate([-10, 0, 0]) //center meridian
.center([10, 52]) //longitude, latitude
.scale(width1 * 1.55); //scale
const path = d3.geoPath().projection(projection);
const svg = d3.select("#Map").append("svg")
d3.json('/Europe1.geo.json')
.then(topology => {
svg.selectAll(".country")
.data(topology.features)
.enter()
.append("path")
.attr("class", ".country")
.attr("d", path)
var svgMarkup = svg.node().outerHTML;
var loader = new SVGLoader();
var svgData = loader.parse(svgMarkup);
var svgGroup = new THREE.Group();
svgData.paths.forEach((path, i) => {
var shapes = path.toShapes(true);
shapes.forEach((shape, j) => {
var geomSVG = new THREE.ExtrudeBufferGeometry(shape, {
depth: 10,
bevelEnabled: false
})
//needed for click event!
// var materialSVG = new THREE.MeshLambertMaterial({
// color: 0xFFFFFF,
// transparent: true,
// opacity: 0,
// });
//var meshSVG = new THREE.Mesh(geomSVG, materialSVG);
//svgGroup.add(meshSVG);
//create borders
var borderMaterial = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 3 })
var borderGeometry = new THREE.EdgesGeometry(geomSVG, 15);
var bordermesh = new THREE.LineSegments(borderGeometry, borderMaterial);
svgGroup.add(bordermesh);
})
})
var axesHelper = new THREE.AxesHelper(50000);
scene.add(axesHelper);
svgGroup.rotateX(Math.PI / 2)
scene.add(svgGroup);
svg.remove();
})
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
I am sure that this is the correct projection.
ETRS_1989_LAEA projection
The problem has to be in the scale but I am not capable of scaling it perfectly in order to fit on the map. The best I could achieve:
I am new to threejs and this seems impossible to me, I would really appreaciate your help!

cannon.js object is flipping end to end

I'm working on a little three.js scene in which I want to drive a car down a road. The trouble is my car is flipping end to end each frame, instead of rolling on its tires:
Does anyone know how I can make my car roll with cannon.js? Any pointers would be hugely helpful. For the sake of preservation, here's my raw scene:
var carBody,
floorBody,
pressed = {},
rotation = 0,
clock = new THREE.Clock(),
loader = new THREE.TextureLoader(),
container = document.querySelector('body'),
w = container.clientWidth,
h = container.clientHeight,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(75, w/h, 0.1, 100000),
controls = new THREE.TrackballControls(camera, container),
renderConfig = {antialias: true, alpha: true},
renderer = new THREE.WebGLRenderer(renderConfig);
controls.target = new THREE.Vector3(0, 0, 0.75);
controls.panSpeed = 0.4;
camera.position.set(0,80,-4900);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(w, h);
container.appendChild(renderer.domElement);
window.addEventListener('resize', function() {
w = container.clientWidth;
h = container.clientHeight;
camera.aspect = w/h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
})
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
if (car && carBody && carBody.position) {
moveCar();
updatePhysics();
moveCamera();
//controls.update();
}
}
function getPlane(img, w, h, wrap) {
var texture = loader.load(img);
if (wrap) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(10, 10);
}
var material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
});
var geometry = new THREE.PlaneGeometry(w, h, 10, 10);
return new THREE.Mesh(geometry, material);
}
function getSides() {
var d = 300;
var group = new THREE.Group();
for (var i=0; i<2; i++) {
var plane = getPlane('asphalt.jpg', 10000, 200, true);
plane.position.y = 100;
plane.rotation.set(Math.PI/2, -Math.PI/2, Math.PI/2);
plane.position.x = i == 0 ? -d : d;
group.add(plane);
}
return group;
}
function getSky() {
var directions = ['right', 'left', 'top', 'bottom', 'front', 'back'];
var geometry = new THREE.BoxGeometry(50000, 50000, 50000);
var materialArray = [];
for (var i=0; i<6; i++)
materialArray.push( new THREE.MeshBasicMaterial({
map: loader.load(directions[i] + '.bmp'),
side: THREE.BackSide
}));
return new THREE.Mesh( geometry, materialArray );
}
function getCar() {
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load('car.mtl', function(mat) {
mat.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(mat);
objLoader.load('car.obj', function(obj) {
window.car = obj;
car.scale.set(0.1, 0.1, 0.1);
obj.position.set(0, 0, -4800);
scene.add(obj);
})
})
}
function getPhysics() {
world = new CANNON.World();
world.gravity.set(0, -100, 0); // earth = -9.82 m/s
world.broadphase = new CANNON.NaiveBroadphase();
world.broadphase.useBoundingBoxes = true;
var solver = new CANNON.GSSolver();
solver.iterations = 7;
solver.tolerance = 0.1;
world.solver = solver;
world.quatNormalizeSkip = 0;
world.quatNormalizeFast = false;
world.defaultContactMaterial.contactEquationStiffness = 1e9;
world.defaultContactMaterial.contactEquationRelaxation = 4;
return world;
}
function addPhysics() {
var m = getPhysicsMaterial();
carBody = new CANNON.Body({
mass: 10,
material: m,
shape: new CANNON.Sphere(30),
linearDamping: 0.6,
angularDamping: 0.8,
position: new CANNON.Vec3(0, 30, -4900)
});
world.addBody(carBody);
// floor
var q = floor.quaternion;
var floorBody = new CANNON.Body({
mass: 0, // mass = 0 makes the body static
material: m,
shape: new CANNON.Plane(),
quaternion: new CANNON.Quaternion(-q._x, q._y, q._z, q._w)
});
world.addBody(floorBody);
}
function getPhysicsMaterial() {
var m = new CANNON.Material('slipperyMaterial');
var c = new CANNON.ContactMaterial(m, m, {
friction: 0.3,
restitution: 0.3,
})
world.addContactMaterial(c);
return m;
}
function moveCar() {
var delta = clock.getDelta(); // seconds
var moveDistance = 2000 * delta; // n pixels per second
// set roll sensitivity
var sensitivity = 1.5;
var rotateAngle = Math.PI / 2 * delta * sensitivity;
// determine the direction to travel
var p = carBody.position;
var dir = new THREE.Vector3(p.x, p.y, p.z);
dir.sub(camera.position).normalize(); // vector b/w camera and car
if (pressed['W'] || pressed['ARROWUP']) {
carBody.velocity.x += moveDistance * dir.x;
carBody.velocity.z += moveDistance * dir.z;
}
if (pressed['S'] || pressed['ARROWDOWN']) {
carBody.velocity.x -= moveDistance * dir.x;
carBody.velocity.z -= moveDistance * dir.z;
}
if (pressed['A'] || pressed['ARROWLEFT']) {
rotation += rotateAngle;
}
if (pressed['D'] || pressed['ARROWRIGHT']) {
rotation -= rotateAngle;
}
if (pressed[' ']) {
carBody.velocity.y = 10;
}
}
function updatePhysics() {
world.step(1/60);
car.position.copy(carBody.position);
car.quaternion.copy(carBody.quaternion);
}
function moveCamera() {
var rotZ = Math.cos(rotation);
var rotX = Math.sin(rotation);
var distance = 100;
camera.position.x = carBody.position.x - (distance * rotX);
camera.position.y = carBody.position.y + 20;
camera.position.z = carBody.position.z - (distance * rotZ);
camera.lookAt(car.position);
}
window.addEventListener('keydown', function(e) {
pressed[e.key.toUpperCase()] = true;
})
window.addEventListener('keyup', function(e) {
pressed[e.key.toUpperCase()] = false;
})
/**
* Add elems
**/
var light = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(light);
var geometry = new THREE.PlaneGeometry(10000, 10000);
var material = new THREE.MeshBasicMaterial();
var floor = new THREE.Mesh(geometry, material);
floor.rotation.x = Math.PI / 2;
scene.add(floor);
var street = getPlane('asphalt.jpg', 10000, 1000, true);
street.rotation.x = Math.PI/2;
street.rotation.z = Math.PI/2;
scene.add(street);
var sides = getSides();
scene.add(sides);
var sky = getSky();
scene.add(sky);
var car = getCar();
var world = getPhysics();
addPhysics();
render();
html,
body {
width: 100%;
height: 100%;
background: #aaa;
}
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.js'></script>
<script src='https://raw.githack.com/mrdoob/three.js/master/examples/js/loaders/ObjLoader.js'></script>
<script src='https://raw.githack.com/mrdoob/three.js/master/examples/js/loaders/MTLLoader.js'></script>
<script src='https://raw.githack.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js'></script>

Rotating an 3D object around it's y-axis in three.js

I just started exploring three.js and have been trying to adapt a project I found.
I would like to know if it would be possible to have the globe object rotate around it's y-axis with minor additions to the code or whether it has to be rewritten from the ground up.
var canvas = document.querySelector('canvas');
var width = canvas.offsetWidth,
height = canvas.offsetHeight;
var colors = [
new THREE.Color(0xac1122),
new THREE.Color(0x96789f),
new THREE.Color(0x535353)];
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
renderer.setSize(width, height);
renderer.setClearColor(0xffffff);
var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = 6;
var camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 2000);
camera.position.set(0, 0, 350);
var galaxy = new THREE.Group();
scene.add(galaxy);
// Create dots
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
var dotTexture = loader.load("img/dotTexture.png");
var dotsAmount = 3000;
var dotsGeometry = new THREE.Geometry();
var positions = new Float32Array(dotsAmount * 3);
var sizes = new Float32Array(dotsAmount);
var colorsAttribute = new Float32Array(dotsAmount * 3);
for (var i = 0; i < dotsAmount; i++) {
var vector = new THREE.Vector3();
vector.color = Math.floor(Math.random() * colors.length);
vector.theta = Math.random() * Math.PI * 2;
vector.phi =
(1 - Math.sqrt(Math.random())) *
Math.PI /
2 *
(Math.random() > 0.5 ? 1 : -1);
vector.x = Math.cos(vector.theta) * Math.cos(vector.phi);
vector.y = Math.sin(vector.phi);
vector.z = Math.sin(vector.theta) * Math.cos(vector.phi);
vector.multiplyScalar(120 + (Math.random() - 0.5) * 5);
vector.scaleX = 5;
if (Math.random() > 0.5) {
moveDot(vector, i);
}
dotsGeometry.vertices.push(vector);
vector.toArray(positions, i * 3);
colors[vector.color].toArray(colorsAttribute, i*3);
sizes[i] = 5;
}
function moveDot(vector, index) {
var tempVector = vector.clone();
tempVector.multiplyScalar((Math.random() - 0.5) * 0.2 + 1);
TweenMax.to(vector, Math.random() * 3 + 3, {
x: tempVector.x,
y: tempVector.y,
z: tempVector.z,
yoyo: true,
repeat: -1,
delay: -Math.random() * 3,
ease: Power0.easeNone,
onUpdate: function () {
attributePositions.array[index*3] = vector.x;
attributePositions.array[index*3+1] = vector.y;
attributePositions.array[index*3+2] = vector.z;
}
});
}
var bufferWrapGeom = new THREE.BufferGeometry();
var attributePositions = new THREE.BufferAttribute(positions, 3);
bufferWrapGeom.addAttribute('position', attributePositions);
var attributeSizes = new THREE.BufferAttribute(sizes, 1);
bufferWrapGeom.addAttribute('size', attributeSizes);
var attributeColors = new THREE.BufferAttribute(colorsAttribute, 3);
bufferWrapGeom.addAttribute('color', attributeColors);
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: dotTexture
}
},
vertexShader: document.getElementById("wrapVertexShader").textContent,
fragmentShader: document.getElementById("wrapFragmentShader").textContent,
transparent:true
});
var wrap = new THREE.Points(bufferWrapGeom, shaderMaterial);
scene.add(wrap);
// Create white segments
var segmentsGeom = new THREE.Geometry();
var segmentsMat = new THREE.LineBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.3,
vertexColors: THREE.VertexColors
});
for (i = dotsGeometry.vertices.length - 1; i >= 0; i--) {
vector = dotsGeometry.vertices[i];
for (var j = dotsGeometry.vertices.length - 1; j >= 0; j--) {
if (i !== j && vector.distanceTo(dotsGeometry.vertices[j]) < 12) {
segmentsGeom.vertices.push(vector);
segmentsGeom.vertices.push(dotsGeometry.vertices[j]);
segmentsGeom.colors.push(colors[vector.color]);
segmentsGeom.colors.push(colors[vector.color]);
}
}
}
var segments = new THREE.LineSegments(segmentsGeom, segmentsMat);
galaxy.add(segments);
var hovered = [];
var prevHovered = [];
function render(a) {
var i;
dotsGeometry.verticesNeedUpdate = true;
segmentsGeom.verticesNeedUpdate = true;
raycaster.setFromCamera( mouse, camera );
var intersections = raycaster.intersectObjects([wrap]);
hovered = [];
if (intersections.length) {
for(i = 0; i < intersections.length; i++) {
var index = intersections[i].index;
hovered.push(index);
if (prevHovered.indexOf(index) === -1) {
onDotHover(index);
}
}
}
for(i = 0; i < prevHovered.length; i++){
if(hovered.indexOf(prevHovered[i]) === -1){
mouseOut(prevHovered[i]);
}
}
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
renderer.render(scene, camera);
}
function onDotHover(index) {
dotsGeometry.vertices[index].tl = new TimelineMax();
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 1, {
scaleX: 10,
ease: Elastic.easeOut.config(2, 0.2),
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function mouseOut(index) {
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 0.4, {
scaleX: 5,
ease: Power2.easeOut,
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function onResize() {
canvas.style.width = '';
canvas.style.height = '';
width = canvas.offsetWidth;
height = canvas.offsetHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
var mouse = new THREE.Vector2(-100,-100);
function onMouseMove(e) {
var canvasBounding = canvas.getBoundingClientRect();
mouse.x = ((e.clientX - canvasBounding.left) / width) * 2 - 1;
mouse.y = -((e.clientY - canvasBounding.top) / height) * 2 + 1;
}
TweenMax.ticker.addEventListener("tick", render);
window.addEventListener("mousemove", onMouseMove);
var resizeTm;
window.addEventListener("resize", function(){
resizeTm = clearTimeout(resizeTm);
resizeTm = setTimeout(onResize, 200);
});
Codepen here - https://codepen.io/quickwaste/pen/PaGPdw
Thanks.
(A stretch goal would be to have the camera move in response to mouse movement)
Simply add galaxy.rotateY(0.005 * Math.PI); to render(), right before renderer.render(scene, camera) call, like this:
// pulled from the CodePen
function render(a) {
// ... omitted for brevity
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
galaxy.rotateY(0.005 * Math.PI);
renderer.render(scene, camera);
}
I used a multiplier of 0.005 to give the globe a nice, lazy spin.
The 'galaxy' object is a THREE.Group, a wrapper of sorts for collections of THREE.Object3D objects. The Object3D has all sorts of nifty functions to help rotate, translate, and transform 3D objects. The rotateY() will spin the model around its local y-axis.

splines arc on three js globe not working

I've been following a couple of examples on mapping arcs around a three.js globe. I've nearly got this to work although I'm having trouble getting the math correct and the resulting projection is incorrect. I'd appreciate anyone taking a look at the code and telling me what I'm doing wrong. Thanks
import oHoverable from 'o-hoverable';
import d3 from 'd3';
import oHeader from 'o-header';
import THREE from 'three.js';
// var OrbitControls = require('three-orbit-controls')(THREE);
// console.log("orbitControls=",OrbitControls);
oHoverable.init(); // makes hover effects work on touch devices
document.addEventListener('DOMContentLoaded', function () {
oHoverable.init(); // makes hover effects work on touch devices
var dataset=spreadsheet.data
console.log(dataset)
// This let's you inspect the resulting image in the console.
//console.log(canvas.node().toDataURL()); //F
var startLon=45
var startLat=75
var endLon=0
var endLat=0
var posX = 200;
var posY = 600;
var posZ = 1800;
var width = document.getElementById('main').getBoundingClientRect().width;
var height = 600;
var FOV = 45;
var NEAR = 2;
var FAR = 4000;
var controls;
// some global variables and initialization code
// simple basic renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor( 0x000000, 1);
// add it to the target element
var globeDiv = document.getElementById("globeDiv");
globeDiv.appendChild(renderer.domElement);
// setup a camera that points to the center
var camera = new THREE.PerspectiveCamera(FOV,width/height,NEAR,FAR);
camera.position.set(posX,posY, posZ);
camera.lookAt(new THREE.Vector3(0,0,0));
// create a basic scene and add the camera
var scene = new THREE.Scene();
scene.add(camera);
//spotlight set up in the same location as the camera
var light = new THREE.DirectionalLight(0xffffff, 1.0, 200 );
light.position.set(posX,posY,posZ);
scene.add(light);
//Add Earth
var radious=650
var line
// var earthGeo=new THREE.SphereGeometry(radious,100,100);
// var earthMat=new THREE.MeshPhongMaterial();
// earthMat.map=THREE.ImageUtils.loadTexture("images/world.jpg");
// earthMat.bumpMap=THREE.ImageUtils.loadTexture("images/bumpmap.jpg");
// earthMat.bumpScale=8;
// earthMat.shininess=10;
// var earthObject = new THREE.Mesh(earthGeo,earthMat);
// scene.add(earthObject);
// //Add clouds
// var cloudGeo=new THREE.SphereGeometry(radious,60,60);
// var cloudsMat=new THREE.MeshPhongMaterial({
// opacity: 0.5,
// transparent: true,
// color: 0xffffff
// });
// cloudsMat.map=THREE.ImageUtils.loadTexture("images/clouds.png");
// var meshClouds = new THREE.Mesh( cloudGeo, cloudsMat );
// meshClouds.scale.set(1.015, 1.015, 1.015 );
// scene.add(meshClouds);
//Add lines
var root = new THREE.Object3D();
for (var i = 0; i < dataset.length; i++) {
endLon=dataset[i].lng;
endLat=dataset[i].lat;
makeCurve(startLat,startLon,endLat,endLon);
root.add(line);
};
scene.add(root)
function makeCurve(startLon,startLat,endLon,endLat){
console.log("makeCurve",startLon,startLat,endLon,endLat);
var phiFrom = startLon * Math.PI / 180;
var thetaFrom = (startLat+90) * Math.PI / 180;
//calculates "from" point
var xF = radious * Math.cos(phiFrom) * Math.sin(thetaFrom);
var yF = radious * Math.sin(phiFrom);
var zF = radious * Math.cos(phiFrom) * Math.cos(thetaFrom);
phiFrom = endLon * Math.PI / 180;
thetaFrom = (endLat+90) * Math.PI / 180;
//calculates "from" point
var xT = radious * Math.cos(phiFrom) * Math.sin(thetaFrom);
var yT = radious * Math.sin(phiFrom);
var zT = radious * Math.cos(phiFrom) * Math.cos(thetaFrom);
//Sets up vectors
var vF = new THREE.Vector3(xF, yF, zF);
var vT = new THREE.Vector3(xT, yT, zT);
var dist = vF.distanceTo(vT);
// here we are creating the control points for the first ones.
// the 'c' in front stands for control.
var cvT = vT.clone();
var cvF = vF.clone();
// get the half point of the vectors points.
var xC = ( 0.5 * (vF.x + vT.x) );
var yC = ( 0.5 * (vF.y + vT.y) );
var zC = ( 0.5 * (vF.z + vT.z) );
// then we create a vector for the midpoints.
var subdivisions = 100;
var geometry = new THREE.Geometry();
var curve = new THREE.QuadraticBezierCurve3();
curve.v0 = new THREE.Vector3(xF, yF, zF);
curve.v1 = new THREE.Vector3(xT, yT, zT);
curve.v2 = new THREE.Vector3(xC, yC, zC);
for (var i = 0; i < subdivisions; i++) {
geometry.vertices.push( curve.getPoint(i / subdivisions) )
}
var material = new THREE.LineBasicMaterial( { color: 0xf2c0a4, linewidth: 2 } );
line = new THREE.Line(geometry, material);
}
// controls = new THREE.OrbitControls( camera );
render();
function render() {
var timer = Date.now() * 0.0001;
// camera.position.x=(Math.cos(timer)*1800);
// camera.position.z=(Math.sin(timer)*1800);
camera.lookAt( scene.position );
// light.position.x = (Math.cos(timer)*2000);
// light.position.z = (Math.sin(timer)*2000);
light.lookAt(scene.position);
//earthObject.rotation.y += 0.0014;
root.rotation.y += 0.0014;
//meshClouds.rotation.y += 0.001;
renderer.render(scene,camera);
requestAnimationFrame(render );
}
});
data sample is as follows and is loaded elsewhere
ftlabel,imfcode,lat,lng
Afghanistan,512,33,66
Albania,914,41,20
Algeria,612,28,3
Angola,614,-12.5,18.5
Argentina,213,-34,-64
Armenia,911,40,45
Aruba,314,12.5,-69.97
Australia,193,-25,135
Austria,122,47.33,13.33
Azerbaijan,912,40.5,47.5
Bahamas,313,24,-76
Bahrain,419,26,50.5
Bangladesh,513,24,90
Barbados,316,13.17,-59.53
Belarus,913,53,28
Belgium,124,50.83,4
Belize,339,17.25,-88.75
Benin,638,9.5,2.25
Bermuda,319,32.33,-64.75
Bolivia,218,-17,-65
Bosnia and Herzegovina,963,44.25,17.83
Brazil,223,-10,-55
Brunei,516,4.5,114.67
Bulgaria,918,43,25
Burkina Faso,748,13,-2
Burundi,618,-3.5,30
Cambodia,522,13,105
Cameroon,622,6,12
Canada,156,60,-96
Cape Verde,624,16,-24
Central African Republic,626,7,21
Chad,628,15,19
Chile,228,-30,-71
China,924,35,105
Colombia,233,4,-72
Comoros,632,-12.17,44.25
Costa Rica,238,10,-84
Cote d'Ivoire,662,8,-5
Croatia,960,45.17,15.5
Cuba,928,22,-79.5
Cleaned up the maths a bit but basically I was passing the lon and lat to the function which was receiving them the wrong way round. Code now working and looks like this
import oHoverable from 'o-hoverable';
import d3 from 'd3';
import oHeader from 'o-header';
import THREE from 'three.js';
oHoverable.init(); // makes hover effects work on touch devices
document.addEventListener('DOMContentLoaded', function () {
oHoverable.init(); // makes hover effects work on touch devices
var dataset=spreadsheet.data
//console.log(dataset)
var startLat=38
var startLon=-100
var endLon=0
var endLat=0
var posX = 200;
var posY = 600;
var posZ = 1800;
var width = document.getElementById('main').getBoundingClientRect().width;
var height = 600;
var FOV = 45;
var NEAR = 2;
var FAR = 4000;
var controls;
// some global variables and initialization code
// simple basic renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor( 0x000000, 1);
// add it to the target element
var globeDiv = document.getElementById("globeDiv");
globeDiv.appendChild(renderer.domElement);
// setup a camera that points to the center
var camera = new THREE.PerspectiveCamera(FOV,width/height,NEAR,FAR);
camera.position.set(posX,posY, posZ);
camera.lookAt(new THREE.Vector3(0,0,0));
// create a basic scene and add the camera
var scene = new THREE.Scene();
scene.add(camera);
//spotlight set up in the same location as the camera
var light = new THREE.DirectionalLight(0xffffff, 1.0, 200 );
light.position.set(posX,posY,posZ);
scene.add(light);
//Add Earth
var radius=600
var curveObject
var earthGeo=new THREE.SphereGeometry(radius,100,100);
var earthMat=new THREE.MeshPhongMaterial();
earthMat.map=THREE.ImageUtils.loadTexture("images/world.jpg");
earthMat.bumpMap=THREE.ImageUtils.loadTexture("images/bumpmap.jpg");
earthMat.bumpScale=8;
earthMat.shininess=10;
var earthObject = new THREE.Mesh(earthGeo,earthMat);
scene.add(earthObject);
//Add clouds
var cloudGeo=new THREE.SphereGeometry(radius,60,60);
var cloudsMat=new THREE.MeshPhongMaterial({
opacity: 0.5,
transparent: true,
color: 0xffffff
});
cloudsMat.map=THREE.ImageUtils.loadTexture("images/clouds.png");
var meshClouds = new THREE.Mesh( cloudGeo, cloudsMat );
meshClouds.scale.set(1.015, 1.015, 1.015 );
scene.add(meshClouds);
//Add lines
var lineObject = new THREE.Object3D();
for (var i = 0; i < dataset.length; i++) {
endLon=dataset[i].lng;
endLat=dataset[i].lat;
makeCurve(startLon,startLat,endLon,endLat,i);
lineObject.add(curveObject);
};
scene.add(lineObject)
// takes lon lat and a radius and turns that into vector
function to3DVector(lon, lat, radius) {
var phi = lat * Math.PI / 180;
var theta = (lon + 90) * Math.PI / 180;
var xF = radius * Math.cos(phi) * Math.sin(theta);
var yF = radius * Math.sin(phi);
var zF = radius * Math.cos(phi) * Math.cos(theta);
return new THREE.Vector3(xF, yF, zF);
}
function makeCurve(startLon,startLat,endLon,endLat,i){
var widthScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d.imfcode; })])
.range([1, 12]);
var vF = to3DVector(startLon, startLat, radius);
var vT = to3DVector(endLon, endLat, radius);
// then you get the half point of the vectors points.
var xC = ( 0.5 * (vF.x + vT.x) );
var yC = ( 0.5 * (vF.y + vT.y) );
var zC = ( 0.5 * (vF.z + vT.z) );
// then we create a vector for the midpoints.
var mid = new THREE.Vector3(xC, yC, zC);
var dist = vF.distanceTo(vT);
// here we are creating the control points for the first ones.
// the 'c' in front stands for control.
var cvT = vT.clone();
var cvF = vF.clone();
var smoothDist = map(dist, 0, 10, 0, 15/dist );
console.log(smoothDist);
mid.setLength( radius * smoothDist );
cvT.add(mid);
cvF.add(mid);
cvT.setLength( radius * smoothDist );
cvF.setLength( radius * smoothDist );
var curve = new THREE.CubicBezierCurve3( vF, cvF, cvT, vT );
var geometry2 = new THREE.Geometry();
geometry2.vertices = curve.getPoints( 50 );
var material2 = new THREE.LineBasicMaterial( { transparent: true,opacity :0.6, color : 0xff0000,linewidth:widthScale(dataset[i].imfcode)} );
// Create the final Object3d to add to the scene
curveObject = new THREE.Line( geometry2, material2 );
function map(value, low1, high1, low2, high2) {
return low2 + (high2 - low2) * (value - low1) / (high1 - low1);
}
}
render();
function render() {
var timer = Date.now() * 0.0001;
// camera.position.x=(Math.cos(timer)*1800);
// camera.position.z=(Math.sin(timer)*1800);
camera.lookAt( scene.position );
// light.position.x = (Math.cos(timer)*2000);
// light.position.z = (Math.sin(timer)*2000);
light.lookAt(scene.position);
earthObject.rotation.y += 0.0005;
lineObject.rotation.y += 0.0005;
meshClouds.rotation.y += 0.0012;
renderer.render(scene,camera);
requestAnimationFrame(render );
}
});

Three.js: How to animate particles along a line

I'm trying to animate particles along a path similar to this chrome expriement: http://armsglobe.chromeexperiments.com/
I've tried digging into the source of this project, and what I've groked so far is that they are using a built in curve method .getPoitns() to generate about 30 points on their lines.
Is there a better example on what I'm trying to accomplish? Is there a method for getting points on the line than using the .lerp() method 30 times to get 30 points? Should I just use TWEEN animations?
Any help or direction would be appreciated.
I've figured out a solution, not sure if it's the best or not, but it works well.
You can find a demo on JsFiddle: https://jsfiddle.net/L4beLw26/
//First create the line that we want to animate the particles along
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(-800, 0, -800));
geometry.vertices.push(new THREE.Vector3(800, 0, 0));
var line = new THREE.Line(geometry, material);
var startPoint = line.geometry.vertices[0];
var endPoint = line.geometry.vertices[1];
scene.add(line);
//next create a set of about 30 animation points along the line
var animationPoints = createLinePoints(startPoint, endPoint);
//add particles to scene
for ( i = 0; i < numParticles; i ++ ) {
var desiredIndex = i / numParticles * animationPoints.length;
var rIndex = constrain(Math.floor(desiredIndex),0,animationPoints.length-1);
var particle = new THREE.Vector3();
var particle = animationPoints[rIndex].clone();
particle.moveIndex = rIndex;
particle.nextIndex = rIndex+1;
if(particle.nextIndex >= animationPoints.length )
particle.nextIndex = 0;
particle.lerpN = 0;
particle.path = animationPoints;
particleGeometry.vertices.push( particle );
}
//set particle material
var pMaterial = new THREE.ParticleBasicMaterial({
color: 0x00FF00,
size: 50,
map: THREE.ImageUtils.loadTexture(
"assets/textures/map_mask.png"
),
blending: THREE.AdditiveBlending,
transparent: true
});
var particles = new THREE.ParticleSystem( particleGeometry, pMaterial );
particles.sortParticles = true;
particles.dynamic = true;
scene.add(particles);
//update function for each particle animation
particles.update = function(){
// var time = Date.now()
for( var i in this.geometry.vertices ){
var particle = this.geometry.vertices[i];
var path = particle.path;
particle.lerpN += 0.05;
if(particle.lerpN > 1){
particle.lerpN = 0;
particle.moveIndex = particle.nextIndex;
particle.nextIndex++;
if( particle.nextIndex >= path.length ){
particle.moveIndex = 0;
particle.nextIndex = 1;
}
}
var currentPoint = path[particle.moveIndex];
var nextPoint = path[particle.nextIndex];
particle.copy( currentPoint );
particle.lerp( nextPoint, particle.lerpN );
}
this.geometry.verticesNeedUpdate = true;
};
function createLinePoints(startPoint, endPoint){
var numPoints = 30;
var returnPoints = [];
for(i=0; i <= numPoints; i ++){
var thisPoint = startPoint.clone().lerp(endPoint, i/numPoints);
returnPoints.push(thisPoint);
}
return returnPoints;
}
function constrain(v, min, max){
if( v < min )
v = min;
else
if( v > max )
v = max;
return v;
}
and then in the animation loop:
particles.update();
I don't know if anyone else can't see the snippets working, but I took the answer that jigglebilly provided and put it into a full html page and is working. Just so that if you want to see a working version. `
Particle Test
<script src="../js/three.js"></script>
<script type="text/javascript">
var renderer = new THREE.WebGLRenderer( { antialias: true } );
var camera = new THREE.PerspectiveCamera( 45, (window.innerWidth) / (window.innerHeight), 100, 10000);
var container = document.getElementById("containerElement");
var numParticles = 40;
container.appendChild( renderer.domElement );
var scene = new THREE.Scene();
var material = new THREE.LineBasicMaterial({color: 0x0000ff });
//First create the line that we want to animate the particles along
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(-800, 0, -800));
geometry.vertices.push(new THREE.Vector3(800, 0, 0));
var line = new THREE.Line(geometry, material);
var startPoint = line.geometry.vertices[0];
var endPoint = line.geometry.vertices[1];
scene.add(line);
//next create a set of about 30 animation points along the line
var animationPoints = createLinePoints(startPoint, endPoint);
var particleGeometry = new THREE.Geometry();
//add particles to scene
for ( i = 0; i < numParticles; i ++ ) {
var desiredIndex = i / numParticles * animationPoints.length;
var rIndex = constrain(Math.floor(desiredIndex),0,animationPoints.length-1);
var particle = new THREE.Vector3();
var particle = animationPoints[rIndex].clone();
particle.moveIndex = rIndex;
particle.nextIndex = rIndex+1;
if(particle.nextIndex >= animationPoints.length )
particle.nextIndex = 0;
particle.lerpN = 0;
particle.path = animationPoints;
particleGeometry.vertices.push( particle );
}
//set particle material
var pMaterial = new THREE.ParticleBasicMaterial({
color: 0x00FF00,
size: 50,
blending: THREE.AdditiveBlending,
transparent: true
});
var particles = new THREE.ParticleSystem( particleGeometry, pMaterial );
particles.sortParticles = true;
particles.dynamic = true;
scene.add(particles);
function UpdateParticles(){
// var time = Date.now()
for( var i = 0; i < particles.geometry.vertices.length; i++ ){
var particle = particles.geometry.vertices[i];
var path = particle.path;
particle.lerpN += 0.05;
if(particle.lerpN > 1){
particle.lerpN = 0;
particle.moveIndex = particle.nextIndex;
particle.nextIndex++;
if( particle.nextIndex >= path.length ){
particle.moveIndex = 0;
particle.nextIndex = 1;
}
}
var currentPoint = path[particle.moveIndex];
var nextPoint = path[particle.nextIndex];
particle.copy( currentPoint );
particle.lerp( nextPoint, particle.lerpN );
}
particles.geometry.verticesNeedUpdate = true;
};
animate();
function createLinePoints(startPoint, endPoint){
var numPoints = 30;
var returnPoints = [];
for(i=0; i <= numPoints; i ++){
var thisPoint = startPoint.clone().lerp(endPoint, i/numPoints);
returnPoints.push(thisPoint);
}
return returnPoints;
}
function constrain(v, min, max){
if( v < min )
v = min;
else
if( v > max )
v = max;
return v;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
UpdateParticles();
}
</script>
`
This uses three.js R67.

Categories

Resources