The code below is supposed to generate a cube and some dots (belonging to a torus). I can see the cube only. I have searched the dots for a couple of hours, but nothing.
// just a cube
cube = new THREE.Mesh(
new THREE.CubeGeometry(50, 50, 50),
new THREE.MeshNormalMaterial({ wireframe: true }));
// a mesh of the torus
function TorusMesh(R, r, nx, ny) {
var vertices = new Array(nx);
var normals = new Array(nx);
for (var i = 0; i < nx; i++) {
vertices[i] = new Array(ny);
normals[i] = new Array(ny);
var u = i / nx * 2 * Math.PI;
var cos_u = Math.cos(u);
var sin_u = Math.sin(u);
var cx = R * cos_u;
var cy = R * sin_u;
for (var j = 0; j < ny; j++) {
var v = j / ny * 2 * Math.PI;
var rcos_v = r * Math.cos(v);
var rsin_v = r * Math.sin(v);
vertices[i][j] = new THREE.Vector3(
cx + rcos_v * cos_u,
cy + rcos_v * sin_u,
rsin_v
);
normals[i][j] = new THREE.Vector3(
rcos_v * cos_u,
rcos_v * sin_u,
rsin_v
);
}
}
var faces = Array(4);
faces[0] = Array(2 * nx * ny);
faces[1] = Array(2 * nx * ny);
for (var i = 0; i < nx; i++) {
var ip1 = (i == nx - 1 ? 0 : i + 1);
for (var j = 0; j < ny; j++) {
var jp1 = (j == ny - 1 ? 0 : j + 1);
faces[0] = [
ip1 * ny + j,
i * ny + j,
i * ny + jp1,
[normals[ip1][j], normals[i][j], normals[i][jp1]]
];
faces[1] = [
ip1 * ny + j,
i * ny + jp1,
ip1 * ny + jp1,
[normals[ip1][j], normals[i][jp1], normals[ip1][jp1]]
];
var Pair = [faces[0], faces[1]];
}
}
return {
vertices: vertices,
normals: normals
//faces: TODO
}
}
// the vertices as a cloud of dots
var dotGeometry = new THREE.Geometry();
var vertices = TorusMesh(10, 3, 16, 8).vertices;
for (var j = 0; j < 8; j++) {
for (var i = 0; i < 15; i++) {
dotGeometry[j * 15 + i] = vertices[i][j]
}
}
var dotMaterial =
new THREE.PointsMaterial({
size: 5,
sizeAttenuation: false,
color: 0x000000
});
cloud = new THREE.Points(dotGeometry, dotMaterial);
console.log(cloud);
// three js scene
var aspect = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(150, aspect, 1, 10000);
var scene = new THREE.Scene();
camera.position.set(0, 0, 20);
scene.add(camera);
// dat.gui controls -------------------------------------------------
var dgcontrols = new function () {
this.rotationSpeed = 0.001;
this.zoom = 20;
}
var gui = new dat.GUI({ autoplace: false, width: 350 });
gui.add(dgcontrols, 'rotationSpeed').min(0).max(0.005).name("Rotation speed");
var controller_zoom = gui.add(dgcontrols, 'zoom').min(1).max(3000);
controller_zoom.onFinishChange(function (value) {
camera.position.z = value;
});
// the render() function
var renderer = new THREE.WebGLRenderer();
function render() {
renderer.render(scene, camera);
object.rotation.x += dgcontrols.rotationSpeed;
object.rotation.y += dgcontrols.rotationSpeed;
requestAnimFrame(render);
}
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
// add objects to the scene
var object = new THREE.Object3D();
scene.add(cloud);
scene.add(cube);
render()
requestAnimFrame(render);
canvas {
width: 100%;
height: 100%
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.js"></script>
The problem was that you have assigned vertices directly to the geometry object instead of dotGeometry.vertices. If you then change the color of the points to white, you should see the points correctly rendered.
Here is a fiddle with your updated code: https://jsfiddle.net/f2Lommf5/15833/
// just a cube
cube = new THREE.Mesh(
new THREE.CubeGeometry(50, 50, 50),
new THREE.MeshNormalMaterial({ wireframe: true }));
// a mesh of the torus
function TorusMesh(R, r, nx, ny) {
var vertices = new Array(nx);
var normals = new Array(nx);
for (var i = 0; i < nx; i++) {
vertices[i] = new Array(ny);
normals[i] = new Array(ny);
var u = i / nx * 2 * Math.PI;
var cos_u = Math.cos(u);
var sin_u = Math.sin(u);
var cx = R * cos_u;
var cy = R * sin_u;
for (var j = 0; j < ny; j++) {
var v = j / ny * 2 * Math.PI;
var rcos_v = r * Math.cos(v);
var rsin_v = r * Math.sin(v);
vertices[i][j] = new THREE.Vector3(
cx + rcos_v * cos_u,
cy + rcos_v * sin_u,
rsin_v
);
normals[i][j] = new THREE.Vector3(
rcos_v * cos_u,
rcos_v * sin_u,
rsin_v
);
}
}
var faces = Array(4);
faces[0] = Array(2 * nx * ny);
faces[1] = Array(2 * nx * ny);
for (var i = 0; i < nx; i++) {
var ip1 = (i == nx - 1 ? 0 : i + 1);
for (var j = 0; j < ny; j++) {
var jp1 = (j == ny - 1 ? 0 : j + 1);
faces[0] = [
ip1 * ny + j,
i * ny + j,
i * ny + jp1,
[normals[ip1][j], normals[i][j], normals[i][jp1]]
];
faces[1] = [
ip1 * ny + j,
i * ny + jp1,
ip1 * ny + jp1,
[normals[ip1][j], normals[i][jp1], normals[ip1][jp1]]
];
var Pair = [faces[0], faces[1]];
}
}
return {
vertices: vertices,
normals: normals
//faces: TODO
}
}
// the vertices as a cloud of dots
var dotGeometry = new THREE.Geometry();
var vertices = TorusMesh(10, 3, 16, 8).vertices;
for (var j = 0; j < 8; j++) {
for (var i = 0; i < 15; i++) {
dotGeometry.vertices[j * 15 + i] = vertices[i][j]
}
}
var dotMaterial =
new THREE.PointsMaterial({
size: 5,
sizeAttenuation: false,
color: 0xffffff
});
cloud = new THREE.Points(dotGeometry, dotMaterial);
// three js scene
var aspect = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(150, aspect, 1, 10000);
var scene = new THREE.Scene();
camera.position.set(0, 0, 20);
scene.add(camera);
// dat.gui controls -------------------------------------------------
var dgcontrols = new function () {
this.rotationSpeed = 0.001;
this.zoom = 20;
}
var gui = new dat.GUI({ autoplace: false, width: 350 });
gui.add(dgcontrols, 'rotationSpeed').min(0).max(0.005).name("Rotation speed");
var controller_zoom = gui.add(dgcontrols, 'zoom').min(1).max(3000);
controller_zoom.onFinishChange(function (value) {
camera.position.z = value;
});
// the render() function
var renderer = new THREE.WebGLRenderer();
function render() {
renderer.render(scene, camera);
object.rotation.x += dgcontrols.rotationSpeed;
object.rotation.y += dgcontrols.rotationSpeed;
requestAnimFrame(render);
}
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
// add objects to the scene
var object = new THREE.Object3D();
scene.add(cloud);
scene.add(cube);
render()
requestAnimFrame(render);
canvas {
width: 100%;
height: 100%
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.js"></script>
Related
I am creating a game where players can move around from first-person perspective, where the ground is generated with Perlin noise and therefore uneven. I would like to simulate gravity in the game. Hence, a raycasting thing has been implemented, which is supposed to find the player's distance from the ground and stop them from falling when they hit the ground. Here is my code (if the snipper is unclear visit https://3d.211368e.repl.co):
const scene = new THREE.Scene(), camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000000000000), renderer = new THREE.WebGLRenderer(), canvas = renderer.domElement;
camera.rotation.order = "YXZ";
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
document.body.appendChild(canvas);
const light = new THREE.DirectionalLight( 0xffffff, 1);
light.position.set(0, 10000, 0);
light.castShadow = true;
light.shadow.camera.top = 10000;
light.shadow.camera.right = 10000;
light.shadow.camera.bottom = -10000;
light.shadow.camera.left = -10000;
light.shadow.camera.far = 100000;
wwwww
scene.add(light);
var sky = new THREE.Mesh(new THREE.SphereGeometry(100000, 3, 3, 0, Math.PI, 0, Math.PI), new THREE.MeshBasicMaterial({color: 0x579ebb}));
sky.material.side = THREE.BackSide;
sky.rotateX(-Math.PI / 2);
scene.add(sky);
class Vector2{
constructor(x, y){
this.x = x;
this.y = y;
}
dot(other){
return this.x * other.x + this.y * other.y;
}
}
function Shuffle(tab){
for(let e = tab.length-1; e > 0; e--){
let index = Math.round(Math.random() * (e-1)),
temp = tab[e];
tab[e] = tab[index];
tab[index] = temp;
}
}
function MakePermutation(){
let P = [];
for(let i = 0; i < 256; i++){
P.push(i);
}
Shuffle(P);
for(let i = 0; i < 256; i++){
P.push(P[i]);
}
return P;
}
let P = MakePermutation();
function GetConstantVector(v){
let h = v & 3;
if(h == 0) return new Vector2(1.0, 1.0);
if(h == 1) return new Vector2(-1.0, 1.0);
if(h == 2) return new Vector2(-1.0, -1.0);
return new Vector2(1.0, -1.0);
}
function Fade(t){
return ((6 * t - 15) * t + 10) * t ** 3;
}
function Lerp(t, a1, a2){
return a1 + t*(a2-a1);
}
function Noise2D(x, y){
let X = Math.floor(x) & 255;
let Y = Math.floor(y) & 255;
let xf = x - Math.floor(x);
let yf = y - Math.floor(y);
let topRight = new Vector2(xf - 1, yf - 1);
let topLeft = new Vector2(xf, yf - 1);
let bottomRight = new Vector2(xf - 1, yf);
let bottomLeft = new Vector2(xf, yf);
let valueTopRight = P[P[X+1]+Y+1];
let valueTopLeft = P[P[X]+Y+1];
let valueBottomRight = P[P[X+1]+Y];
let valueBottomLeft = P[P[X]+Y];
let dotTopRight = topRight.dot(GetConstantVector(valueTopRight));
let dotTopLeft = topLeft.dot(GetConstantVector(valueTopLeft));
let dotBottomRight = bottomRight.dot(GetConstantVector(valueBottomRight));
let dotBottomLeft = bottomLeft.dot(GetConstantVector(valueBottomLeft));
let u = Fade(xf);
let v = Fade(yf);
return Lerp(u, Lerp(v, dotBottomLeft, dotTopLeft), Lerp(v, dotBottomRight, dotTopRight));
}
const plane = new THREE.Mesh(new THREE.PlaneGeometry(10000, 10000, 500, 500), new THREE.MeshPhongMaterial({color: 0x00aa00}));
plane.rotateX(-Math.PI / 2 + 0.00001);
plane.receiveShadow = true;
for (let y = 0, i = 0; y < 501; y++){
for(let x = 0; x < 501; x++, i++){
let n = 0.0, a = 1.0, f = 0.005;
for (let o = 0; o < 3; o++){
let v = a*Noise2D(x*f, y*f);
n += v;
a *= 0.5;
f *= 2.0;
}
n += 1;
n /= 2;
plane.geometry.vertices[i].z = n * 1000;
}
}
scene.add(plane);
const point = plane.geometry.vertices[Math.floor(Math.random() * 1000)];
camera.position.set(point.x, point.z + 2, point.y);
const geo = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshBasicMaterial({color: 0xff0000}));
geo.castShadow = true;
scene.add(geo);
const render = () => {
requestAnimationFrame(render);
const below = new THREE.Vector3(camera.position.x, -1000000, camera.position.y), cast = new THREE.Raycaster(camera.position, below), intersect = cast.intersectObject(plane);
if (intersect.length > 0){
if (intersect[0].distance < 3) camera.translateY(-1);
}else{
camera.translateY(-1);
}
renderer.render(scene, camera);
}
render();
onmousemove = () => {
if (camera.rotation._x > -0.8 || camera.rotation._y > -0.8){
camera.rotateX(-Math.atan(event.movementY / 300));
camera.rotateY(-Math.atan(event.movementX / 300));
}else{
if (Math.atan(event.movementY / 300) < 0) camera.rotateX(-Math.atan(event.movementY / 300));
if (Math.atan(event.movementX / 300) < 0) camera.rotateY(-Math.atan(event.movementX / 300));
}
camera.rotation.z = 0;
}
onresize = () => {
renderer.setSize(window.innerWidth, window.innerHeight);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
onkeydown = (event) => {
if (event.key == "w") camera.translateZ(-10);
if (event.key == "a") camera.translateX(-1);
if (event.key == "s") camera.translateZ(1);
if (event.key == "d") camera.translateX(1);
if (event.key == "ArrowUp") camera.translateY(1);
if (event.key == "ArrowDown") camera.translateY(-1);
}
body{
margin: 0;
background-color: black;
overflow: hidden;
}
canvas{
border: none;
}
<meta name="viewport" content="width=device-width">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/0949e59f/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/0949e59f/examples/js/utils/SceneUtils.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/0949e59f/examples/js/libs/dat.gui.min.js"></script>
If the ground is not detected at least 3 units below the camera, the player will continue falling. However, sometimes nothing is spotted below the camera, while the player is clearly hovering over the ground. This is extremely frustrating. Is there any reliable alternative method to solve this problem, such as using something other than raycasting? Or is there a bug in the code? TIA
See the Raycaster documentation. The constructor takes the origin, the direction and near and far parameters. So you could do:
const gravityDirection = new THREE.Vector3(0, -1, 0);
cast = new THREE.Raycaster(camera.position, gravityDirection, 0, 3);
and this also makes the distance check redundant, as the far parameter already filters out hits further away than 3 units.
Is it real to fill all polygons? Codepen. As I get it ThreeGeoJSON can not fill polygons, outlines only. Also I've tried Earcut for triangulation.
drawThreeGeo(data, radius, 'sphere', {color: 'yellow' // I want to edit fill color of lands, not outline color})
I suggest you to use better map: countries.geojson
The solution consists of following steps, for each shape:
Put vertices inside of shape, so that when triangulated, it could bend around the globe,
Run https://github.com/mapbox/delaunator to build triangulated mesh,
Step 2 will create triangles outside the shape too, we need to remove them by looking into each triangle, and deciding if it belongs to shape or not,
Bend the triangulated mesh with convertCoordinates
You can test my jsfiddle: http://jsfiddle.net/mmalex/pg5a4132/
Warning: it is quite slow because of high level of detail of input.
The complete solution:
/* Draw GeoJSON
Iterates through the latitude and longitude values, converts the values to XYZ coordinates, and draws the geoJSON geometries.
*/
let TRIANGULATION_DENSITY = 5; // make it smaller for more dense mesh
function verts2array(coords) {
let flat = [];
for (let k = 0; k < coords.length; k++) {
flat.push(coords[k][0], coords[k][1]);
}
return flat;
}
function array2verts(arr) {
let coords = [];
for (let k = 0; k < arr.length; k += 2) {
coords.push([arr[k], arr[k + 1]]);
}
return coords;
}
function findBBox(points) {
let min = {
x: 1e99,
y: 1e99
};
let max = {
x: -1e99,
y: -1e99
};
for (var point_num = 0; point_num < points.length; point_num++) {
if (points[point_num][0] < min.x) {
min.x = points[point_num][0];
}
if (points[point_num][0] > max.x) {
max.x = points[point_num][0];
}
if (points[point_num][1] < min.y) {
min.y = points[point_num][1];
}
if (points[point_num][1] > max.y) {
max.y = points[point_num][1];
}
}
return {
min: min,
max: max
};
}
function isInside(point, vs) {
// ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
var x = point[0],
y = point[1];
var inside = false;
for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
var xi = vs[i][0],
yi = vs[i][1];
var xj = vs[j][0],
yj = vs[j][1];
var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
}
function genInnerVerts(points) {
let res = [];
for (let k = 0; k < points.length; k++) {
res.push(points[k]);
}
let bbox = findBBox(points);
let step = TRIANGULATION_DENSITY;
let k = 0;
for (let x = bbox.min.x + step / 2; x < bbox.max.x; x += step) {
for (let y = bbox.min.y + step / 2; y < bbox.max.y; y += step) {
let newp = [x, y];
if (isInside(newp, points)) {
res.push(newp);
}
k++;
}
}
return res;
}
function removeOuterTriangles(delaunator, points) {
let newTriangles = [];
for (let k = 0; k < delaunator.triangles.length; k += 3) {
let t0 = delaunator.triangles[k];
let t1 = delaunator.triangles[k + 1];
let t2 = delaunator.triangles[k + 2];
let x0 = delaunator.coords[2 * t0];
let y0 = delaunator.coords[2 * t0 + 1];
let x1 = delaunator.coords[2 * t1];
let y1 = delaunator.coords[2 * t1 + 1];
let x2 = delaunator.coords[2 * t2];
let y2 = delaunator.coords[2 * t2 + 1];
let midx = (x0 + x1 + x2) / 3;
let midy = (y0 + y1 + y2) / 3;
let midp = [midx, midy];
if (isInside(midp, points)) {
newTriangles.push(t0, t1, t2);
}
}
delaunator.triangles = newTriangles;
}
var x_values = [];
var y_values = [];
var z_values = [];
var progressEl = $("#progress");
var clickableObjects = [];
var someColors = [0x909090, 0x808080, 0xa0a0a0, 0x929292, 0x858585, 0xa9a9a9];
function drawThreeGeo(json, radius, shape, options) {
var json_geom = createGeometryArray(json);
var convertCoordinates = getConversionFunctionName(shape);
for (var geom_num = 0; geom_num < json_geom.length; geom_num++) {
console.log("Processing " + geom_num + " of " + json_geom.length + " shapes");
// if (geom_num !== 17) continue;
// if (geom_num > 10) break;
if (json_geom[geom_num].type == 'Point') {
convertCoordinates(json_geom[geom_num].coordinates, radius);
drawParticle(y_values[0], z_values[0], x_values[0], options);
} else if (json_geom[geom_num].type == 'MultiPoint') {
for (let point_num = 0; point_num < json_geom[geom_num].coordinates.length; point_num++) {
convertCoordinates(json_geom[geom_num].coordinates[point_num], radius);
drawParticle(y_values[0], z_values[0], x_values[0], options);
}
} else if (json_geom[geom_num].type == 'LineString') {
for (let point_num = 0; point_num < json_geom[geom_num].coordinates.length; point_num++) {
convertCoordinates(json_geom[geom_num].coordinates[point_num], radius);
}
drawLine(y_values, z_values, x_values, options);
} else if (json_geom[geom_num].type == 'Polygon') {
let group = createGroup(geom_num);
let randomColor = someColors[Math.floor(someColors.length * Math.random())];
for (let segment_num = 0; segment_num < json_geom[geom_num].coordinates.length; segment_num++) {
let coords = json_geom[geom_num].coordinates[segment_num];
let refined = genInnerVerts(coords);
let flat = verts2array(refined);
let d = new Delaunator(flat);
removeOuterTriangles(d, coords);
let delaunayVerts = array2verts(d.coords);
for (let point_num = 0; point_num < delaunayVerts.length; point_num++) {
// convertCoordinates(refined[point_num], radius);
convertCoordinates(delaunayVerts[point_num], radius);
}
// drawLine(y_values, z_values, x_values, options);
drawMesh(group, y_values, z_values, x_values, d.triangles, randomColor);
}
} else if (json_geom[geom_num].type == 'MultiLineString') {
for (let segment_num = 0; segment_num < json_geom[geom_num].coordinates.length; segment_num++) {
let coords = json_geom[geom_num].coordinates[segment_num];
for (let point_num = 0; point_num < coords.length; point_num++) {
convertCoordinates(json_geom[geom_num].coordinates[segment_num][point_num], radius);
}
drawLine(y_values, z_values, x_values);
}
} else if (json_geom[geom_num].type == 'MultiPolygon') {
let group = createGroup(geom_num);
let randomColor = someColors[Math.floor(someColors.length * Math.random())];
for (let polygon_num = 0; polygon_num < json_geom[geom_num].coordinates.length; polygon_num++) {
for (let segment_num = 0; segment_num < json_geom[geom_num].coordinates[polygon_num].length; segment_num++) {
let coords = json_geom[geom_num].coordinates[polygon_num][segment_num];
let refined = genInnerVerts(coords);
let flat = verts2array(refined);
let d = new Delaunator(flat);
removeOuterTriangles(d, coords);
let delaunayVerts = array2verts(d.coords);
for (let point_num = 0; point_num < delaunayVerts.length; point_num++) {
// convertCoordinates(refined[point_num], radius);
convertCoordinates(delaunayVerts[point_num], radius);
}
// drawLine(y_values, z_values, x_values, options);
drawMesh(group, y_values, z_values, x_values, d.triangles, randomColor)
}
}
} else {
throw new Error('The geoJSON is not valid.');
}
}
progressEl.text("Complete!");
}
function createGeometryArray(json) {
var geometry_array = [];
if (json.type == 'Feature') {
geometry_array.push(json.geometry);
} else if (json.type == 'FeatureCollection') {
for (var feature_num = 0; feature_num < json.features.length; feature_num++) {
geometry_array.push(json.features[feature_num].geometry);
}
} else if (json.type == 'GeometryCollection') {
for (var geom_num = 0; geom_num < json.geometries.length; geom_num++) {
geometry_array.push(json.geometries[geom_num]);
}
} else {
throw new Error('The geoJSON is not valid.');
}
//alert(geometry_array.length);
return geometry_array;
}
function getConversionFunctionName(shape) {
var conversionFunctionName;
if (shape == 'sphere') {
conversionFunctionName = convertToSphereCoords;
} else if (shape == 'plane') {
conversionFunctionName = convertToPlaneCoords;
} else {
throw new Error('The shape that you specified is not valid.');
}
return conversionFunctionName;
}
function convertToSphereCoords(coordinates_array, sphere_radius) {
var lon = coordinates_array[0];
var lat = coordinates_array[1];
x_values.push(Math.cos(lat * Math.PI / 180) * Math.cos(lon * Math.PI / 180) * sphere_radius);
y_values.push(Math.cos(lat * Math.PI / 180) * Math.sin(lon * Math.PI / 180) * sphere_radius);
z_values.push(Math.sin(lat * Math.PI / 180) * sphere_radius);
}
function convertToPlaneCoords(coordinates_array, radius) {
var lon = coordinates_array[0];
var lat = coordinates_array[1];
var plane_offset = radius / 2;
z_values.push((lat / 180) * radius);
y_values.push((lon / 180) * radius);
}
function drawParticle(x, y, z, options) {
var particle_geom = new THREE.Geometry();
particle_geom.vertices.push(new THREE.Vector3(x, y, z));
var particle_material = new THREE.ParticleSystemMaterial(options);
var particle = new THREE.ParticleSystem(particle_geom, particle_material);
scene.add(particle);
clearArrays();
}
function drawLine(x_values, y_values, z_values, options) {
var line_geom = new THREE.Geometry();
createVertexForEachPoint(line_geom, x_values, y_values, z_values);
var line_material = new THREE.LineBasicMaterial(options);
var line = new THREE.Line(line_geom, line_material);
scene.add(line);
clearArrays();
}
function createGroup(idx) {
var group = new THREE.Group();
group.userData.userText = "_" + idx;
scene.add(group);
return group;
}
function drawMesh(group, x_values, y_values, z_values, triangles, color) {
var geometry = new THREE.Geometry();
for (let k = 0; k < x_values.length; k++) {
geometry.vertices.push(
new THREE.Vector3(x_values[k], y_values[k], z_values[k])
);
}
for (let k = 0; k < triangles.length; k += 3) {
geometry.faces.push(new THREE.Face3(triangles[k], triangles[k + 1], triangles[k + 2]));
}
geometry.computeVertexNormals()
var mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
color: color,
wireframe: true
}));
clickableObjects.push(mesh);
group.add(mesh);
clearArrays();
}
function createVertexForEachPoint(object_geometry, values_axis1, values_axis2, values_axis3) {
for (var i = 0; i < values_axis1.length; i++) {
object_geometry.vertices.push(new THREE.Vector3(values_axis1[i],
values_axis2[i], values_axis3[i]));
}
}
function clearArrays() {
x_values.length = 0;
y_values.length = 0;
z_values.length = 0;
}
var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();
var camera = new THREE.PerspectiveCamera(32, window.innerWidth / window.innerHeight, 0.5, 1000);
var radius = 200;
camera.position.x = 140.7744005681177;
camera.position.y = 160.30950538100814;
camera.position.z = 131.8637122564268;
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
var light = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(light);
var light = new THREE.AmbientLight(0x505050); // soft white light
scene.add(light);
var geometry = new THREE.SphereGeometry(radius, 32, 32);
var material = new THREE.MeshPhongMaterial({
color: 0x1e90ff
});
var sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
var test_json = $.getJSON("https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson", function(data) {
drawThreeGeo(data, radius + 1, 'sphere', {
color: 'yellow'
})
});
var controls = new THREE.TrackballControls(camera);
controls.rotateSpeed *= 0.5;
controls.zoomSpeed *= 0.5;
controls.panSpeed *= 0.5;
controls.minDistance = 10;
controls.maxDistance = 5000;
function render() {
controls.update();
requestAnimationFrame(render);
renderer.setClearColor(0x1e90ff, 1);
renderer.render(scene, camera);
}
render()
function convert_lat_lng(lat, lng, radius) {
var phi = (90 - lat) * Math.PI / 180,
theta = (180 - lng) * Math.PI / 180,
position = new THREE.Vector3();
position.x = radius * Math.sin(phi) * Math.cos(theta);
position.y = radius * Math.cos(phi);
position.z = radius * Math.sin(phi) * Math.sin(theta);
return position;
}
// this will be 2D coordinates of the current mouse position, [0,0] is middle of the screen.
var mouse = new THREE.Vector2();
var hoveredObj; // this objects is hovered at the moment
// Following two functions will convert mouse coordinates
// from screen to three.js system (where [0,0] is in the middle of the screen)
function updateMouseCoords(event, coordsObj) {
coordsObj.x = ((event.clientX - renderer.domElement.offsetLeft + 0.5) / window.innerWidth) * 2 - 1;
coordsObj.y = -((event.clientY - renderer.domElement.offsetTop + 0.5) / window.innerHeight) * 2 + 1;
}
function onMouseMove(event) {
updateMouseCoords(event, mouse);
latestMouseProjection = undefined;
clickedObj = undefined;
raycaster.setFromCamera(mouse, camera); {
var intersects = raycaster.intersectObjects(clickableObjects);
let setGroupColor = function(group, colorHex) {
for (let i = 0; i < group.children.length; i++) {
if (!group.children[i].userData.color) {
group.children[i].userData.color = hoveredObj.parent.children[i].material.color.clone();
group.children[i].material.color.set(colorHex);
group.children[i].material.needsUpdate = true;
}
}
}
let resetGroupColor = function(group) {
// set all shapes of the group to initial color
for (let i = 0; i < group.children.length; i++) {
if (group.children[i].userData.color) {
group.children[i].material.color = group.children[i].userData.color;
delete group.children[i].userData.color;
group.children[i].material.needsUpdate = true;
}
}
}
if (intersects.length > 0) {
latestMouseProjection = intersects[0].point;
// reset colors for previously hovered group
if (hoveredObj) {
resetGroupColor(hoveredObj.parent);
}
hoveredObj = intersects[0].object;
if (!hoveredObj.parent) return;
// set colors for hovered group
setGroupColor(hoveredObj.parent, 0xff0000);
} else {
if (!hoveredObj || !hoveredObj.parent) return;
// nothing is hovered => just reset colors on the last group
resetGroupColor(hoveredObj.parent);
hoveredObj = undefined;
console.log("<deselected>");
}
}
}
window.addEventListener('mousemove', onMouseMove, false);
You'd need to split each country into a separate geometry, use a raycaster to find out which country the mouse is over, then change its material.color. You can see raycasting in action in this example with source code available on the bottom-right corner. The key lines in that example are:
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function render() {
// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
renderer.render( scene, camera );
}
This code should produce a mesh of a torus in three js. I'm pretty sure the maths are correct. However it renders only a piece of torus, or stranger things if I change some parameters. Is there something bad with my practice of THREE.Mesh ?
// the vertices of the mesh and the vertex normals ----------------
var nx = 64;
var ny = 32;
var R = 10; var r = 3;
var Vertices = new Array(nx);
var Normals = new Array(nx);
for (var i = 0; i < nx; i++) {
Vertices[i] = new Array(ny);
Normals[i] = new Array(ny);
var u = i / nx * 2 * Math.PI;
var cos_u = Math.cos(u);
var sin_u = Math.sin(u);
var cx = R * cos_u;
var cy = R * sin_u;
for (var j = 0; j < ny; j++) {
var v = j / ny * 2 * Math.PI;
var rcos_v = r * Math.cos(v);
var rsin_v = r * Math.sin(v);
Vertices[i][j] = new THREE.Vector3(
cx + rcos_v * cos_u,
cy + rcos_v * sin_u,
rsin_v
);
Normals[i][j] = new THREE.Vector3(
rcos_v * cos_u,
rcos_v * sin_u,
rsin_v
);
}
}
// vertices as a dot cloud ----------------------------------------
var dotGeometry = new THREE.Geometry();
for (var i = 0; i < nx; i++) {
for (var j = 0; j < ny; j++) {
dotGeometry.vertices.push(Vertices[i][j]);
}
}
var dotMaterial =
new THREE.PointsMaterial({ size: 1, sizeAttenuation: false });
var cloud = new THREE.Points(dotGeometry, dotMaterial);
// mesh -----------------------------------------------------------
var geom = new THREE.Geometry();
for (var i = 0; i < nx; i++) {
var ip1 = (i == nx - 1 ? 0 : i + 1);
for (var j = 0; j < ny; j++) {
var jp1 = (j == ny - 1 ? 0 : j + 1);
geom.vertices.push(Vertices[i][j]);
geom.vertices.push(Vertices[i][jp1]);
geom.vertices.push(Vertices[ip1][j]);
var vnormals1 =
[Normals[i][j], Normals[i][jp1], Normals[ip1][j]];
geom.faces.push(new THREE.Face3(
i * ny + j,
i * ny + jp1,
ip1 * ny + j,
vnormals1
));
geom.vertices.push(Vertices[i][jp1]);
geom.vertices.push(Vertices[ip1][jp1]);
geom.vertices.push(Vertices[ip1][j]);
var vnormals2 =
[Normals[i][jp1], Normals[ip1][jp1], Normals[ip1][j]];
geom.faces.push(new THREE.Face3(
i * ny + jp1,
ip1 * ny + jp1,
ip1 * ny + j,
vnormals2
));
}
}
var torusMesh = new THREE.Mesh(
geom,
new THREE.MeshNormalMaterial({ wireframe: false }));
// three js scene -------------------------------------------------
var scene = new THREE.Scene();
var aspect = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(50, aspect, 1, 10000);
camera.position.z = 30;
scene.add(camera);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var object = new THREE.Object3D();
object.add(torusMesh);
object.add(cloud);
scene.add(object);
renderer.render(scene, camera);
// animation ---------------------------------------------------------
var isDragging = false;
var previousMousePosition = {
x: 0,
y: 0
};
$(renderer.domElement).on('mousedown', function (e) {
isDragging = true;
}).on('mousemove', function (e) {
var deltaMove = {
x: e.offsetX - previousMousePosition.x,
y: e.offsetY - previousMousePosition.y
};
if (isDragging) {
var deltaRotationQuaternion = new THREE.Quaternion()
.setFromEuler(new THREE.Euler(
Math.PI / 180 * (deltaMove.y * 1),
Math.PI / 180 * (deltaMove.x * 1),
0,
'XYZ'
));
object.quaternion.multiplyQuaternions(deltaRotationQuaternion,
object.quaternion);
}
previousMousePosition = {
x: e.offsetX,
y: e.offsetY
};
});
$(document).on('mouseup', function (e) {
isDragging = false;
});
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function render() {
renderer.render(scene, camera);
requestAnimFrame(render);
}
render();
canvas {
width: 100%;
height: 100%
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js"></script>
I think there were two problems with your code:
When using Geometry, you define faces just by adding objects of type Face3 to the faces array. The vertices of the geometry are defined just once. In your case, you can just do this:
geom.vertices = dotGeometry.vertices;
Besides, the winding order of your faces was not correct. You have to switch the first and third index.
// the vertices of the mesh and the vertex normals ----------------
var nx = 64;
var ny = 32;
var R = 10; var r = 3;
var Vertices = new Array(nx);
var Normals = new Array(nx);
for (var i = 0; i < nx; i++) {
Vertices[i] = new Array(ny);
Normals[i] = new Array(ny);
var u = i / nx * 2 * Math.PI;
var cos_u = Math.cos(u);
var sin_u = Math.sin(u);
var cx = R * cos_u;
var cy = R * sin_u;
for (var j = 0; j < ny; j++) {
var v = j / ny * 2 * Math.PI;
var rcos_v = r * Math.cos(v);
var rsin_v = r * Math.sin(v);
Vertices[i][j] = new THREE.Vector3(
cx + rcos_v * cos_u,
cy + rcos_v * sin_u,
rsin_v
);
Normals[i][j] = new THREE.Vector3(
rcos_v * cos_u,
rcos_v * sin_u,
rsin_v
);
}
}
// vertices as a dot cloud ----------------------------------------
var dotGeometry = new THREE.Geometry();
for (var i = 0; i < nx; i++) {
for (var j = 0; j < ny; j++) {
dotGeometry.vertices.push(Vertices[i][j]);
}
}
var dotMaterial =
new THREE.PointsMaterial({ size: 1, sizeAttenuation: false });
var cloud = new THREE.Points(dotGeometry, dotMaterial);
// mesh -----------------------------------------------------------
var geom = new THREE.Geometry();
geom.vertices = dotGeometry.vertices;
for (var i = 0; i < nx; i++) {
var ip1 = (i == nx - 1 ? 0 : i + 1);
for (var j = 0; j < ny; j++) {
var jp1 = (j == ny - 1 ? 0 : j + 1);
var vnormals1 =
[Normals[i][j], Normals[i][jp1], Normals[ip1][j]];
geom.faces.push(new THREE.Face3(
ip1 * ny + j,
i * ny + jp1,
i * ny + j,
vnormals1
));
var vnormals2 =
[Normals[i][jp1], Normals[ip1][jp1], Normals[ip1][j]];
geom.faces.push(new THREE.Face3(
ip1 * ny + j,
ip1 * ny + jp1,
i * ny + jp1,
vnormals2
));
}
}
var torusMesh = new THREE.Mesh(
geom,
new THREE.MeshNormalMaterial({ wireframe: false }));
// three js scene -------------------------------------------------
var scene = new THREE.Scene();
var aspect = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(50, aspect, 1, 10000);
camera.position.z = 30;
scene.add(camera);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var object = new THREE.Object3D();
object.add(torusMesh);
object.add(cloud);
scene.add(object);
renderer.render(scene, camera);
// animation ---------------------------------------------------------
var isDragging = false;
var previousMousePosition = {
x: 0,
y: 0
};
$(renderer.domElement).on('mousedown', function (e) {
isDragging = true;
}).on('mousemove', function (e) {
var deltaMove = {
x: e.offsetX - previousMousePosition.x,
y: e.offsetY - previousMousePosition.y
};
if (isDragging) {
var deltaRotationQuaternion = new THREE.Quaternion()
.setFromEuler(new THREE.Euler(
Math.PI / 180 * (deltaMove.y * 1),
Math.PI / 180 * (deltaMove.x * 1),
0,
'XYZ'
));
object.quaternion.multiplyQuaternions(deltaRotationQuaternion,
object.quaternion);
}
previousMousePosition = {
x: e.offsetX,
y: e.offsetY
};
});
$(document).on('mouseup', function (e) {
isDragging = false;
});
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
function render() {
renderer.render(scene, camera);
requestAnimFrame(render);
}
render();
canvas {
width: 100%;
height: 100%
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js"></script>
<script
src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E="
crossorigin="anonymous"></script>
Besides, consider to use the approach of TorusBufferGeometry. Moreover, it's much faster to generate a geometry with BufferGeometry than with Geometry.
I am creating a THREEjs animation which I eventually want to sync with audio. (Can't accomplish this currently) I would like to add and remove shaders at specific points. How can I accomplish this most efficiently?
The way I have it set up now, is that I have a mirror shader inside of a function called turnOnMirror and in my render function, I have a conditional statement,
if (audioSrc.context.currentTime > 32.0) { turnOnMirror(); }
The shader looks like this:
var mirror = mirrorPass = new THREE.ShaderPass( THREE.MirrorShader );
mirror.renderToScreen = true;
composer.addPass(mirror);
(I am doing this for 2 reasons!
A: The renderer is the only place I can grab the actual time to make this call. And B: because for whatever reason, my frequencyData array comes back as 0s.
However, when the scene reaches this point, everything slows down extremely. How can I keep the same frame rate and accomplish what I am attempting to at the same time?
A piece of information for you is that the scene works fine at the same constant speed if I just apply the shader without making the call in the render function.
You can view the site here!
And the source code for the main.js is below!
/* ==================== [ Global Variables ] ==================== */
var scene, camera, renderer, aspectRatio;
var stats;
var composer, effect, clock;
var backMesh;
/* ==================== [ Audio Context ] ==================== */
var ctx = new AudioContext();
var audio = document.getElementById('player');
audio.play();
audio.volume = 1;
// audio.crossOrigin = "anonymous";
var audioSrc = ctx.createMediaElementSource(audio);
var analyser = ctx.createAnalyser();
audioSrc.connect(analyser);
audioSrc.connect(ctx.destination);
// frequencyBinCount tells you how many values you'll receive from the analyser
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(frequencyData);
console.log(audioSrc);
console.log(audioSrc.context.currentTime);
console.log(frequencyData);
console.log(analyser.fftSize); // 2048 by default
console.log(analyser.frequencyBinCount); // will give us 1024 data points
analyser.fftSize = 64;
console.log(analyser.frequencyBinCount); // fftSize/2 = 32 data points
/* ==================== [ Set Scene & Camera ] ==================== */
scene = new THREE.Scene();
// scene.fog = new THREE.Fog(0x000000, 0, 1200);
aspectRatio = window.innerWidth / window.innerHeight;
camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 100);
// camera.target = new THREE.Vector3( 10, 10, 10 );
// Set the DOM
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor("#000000");
document.body.appendChild(renderer.domElement);
/* ==================== [ Camera Position ] ==================== */
camera.position.z = 15;
camera.position.y = 0;
/* ==================== [ Point Lights ] ==================== */
var pointLightBlue = new THREE.PointLight("#00ccff", 5, 100, 2);
pointLightBlue.position.set(-10, -40, -10);
scene.add(pointLightBlue);
// var pointLightWhite = new THREE.PointLight( "#ffffff", 1, 0, 1 );
// // pointLightWhite.position.set( -10, 160, -10 );
// pointLightWhite.position.set( 0, 0, 1 );
// scene.add(pointLightWhite);
// camera.add(pointLightWhite);
// var pointLightPink = new THREE.PointLight( "#EE567C", 5, 100, 10 );
// pointLightPink.position.set( 1, 0, -5 );
// scene.add(pointLightPink);
var pointLight = new THREE.PointLight("#A805FA", 2, 100, 40);
pointLight.position.set(40, 0, 40);
scene.add(pointLight);
var light2 = new THREE.PointLight( 0xFFFFFF, 1, 100 );
scene.add( light2 );
light2.position.z = 1000;
var pointLight2 = new THREE.PointLight("#07FAA0", 2, 100, 30);
pointLight2.position.set(-40, 0, -40);
scene.add(pointLight2);
/* ==================== [ Particles ] ==================== */
var getCamera = function() {
return camera;
}
// var texture = new Image();
// texture.src = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/82015/snowflake.png';
// texture.src = './images/particle.png';
//var material = new THREE.ParticleBasicMaterial( { map: new THREE.Texture(texture) } );
var particleCount = 0, particleSystem, particles;
THREE.ImageUtils.crossOrigin = '';
var texture = THREE.ImageUtils.loadTexture('./images/particle.png');
//console.log(texture);
particleCount = 20000,
particles = new THREE.Geometry();
var pMaterial = new THREE.PointCloudMaterial({
color: 0xFFFFFF,
map: texture,
blending: THREE.AdditiveBlending,
depthTest: false,
depthWrite: false,
transparent: true,
opacity: 0.3,
side: THREE.DoubleSide,
size: 1.2
});
for (var i = 0; i < particleCount; i++) {
var pX = Math.random() * 500 - 250,
pY = Math.random() * 500 - 250,
pZ = Math.random() * 500 - 250,
particle = new THREE.Vector3(pX, pY, pZ);
particles.vertices.push(particle);
}
particleSystem = new THREE.ParticleSystem(particles, pMaterial);
particleSystem.sortParticles = false;
particleSystem.frustumCulled = false;
scene.add(particleSystem);
/* ==================== [ Light Beams ] ==================== */
var BEAM_ROT_SPEED = 0.003;
var BEAM_COUNT = 360;
var beamGeometry = new THREE.PlaneBufferGeometry(1, 500, 10, 1);
beamGroup = new THREE.Object3D();
beamMaterial = new THREE.MeshBasicMaterial({
opacity: 0.02,
transparent: true,
});
for (var i = 0; i <= BEAM_COUNT; ++i) {
var beam = new THREE.Mesh(beamGeometry, beamMaterial);
beam.doubleSided = true;
beam.rotation.x = Math.random() * Math.PI;
beam.rotation.y = Math.random() * Math.PI;
beam.rotation.z = Math.random() * Math.PI;
beamGroup.add(beam);
}
scene.add(beamGroup);
beamGroup.translateZ( -5 );
/* ==================== [ Cubes ] ==================== */
var doStrobe = false;
var doShake = false;
var strobeOn = false;
var beatTime = 30;
THREE.ImageUtils.crossOrigin = '';
var imgTextureStripes2 = THREE.ImageUtils.loadTexture( "./images/stripes2.jpg" );
imgTextureStripes2.wrapS = imgTextureStripes2.wrapT = THREE.RepeatWrapping;
imgTextureStripes2.repeat.set( 100, 100 );
backMaterial2 = new THREE.MeshBasicMaterial( {
map:imgTextureStripes2
} );
backMesh2 = new THREE.Mesh( new THREE.SphereGeometry( 1900, 30, 20 ), backMaterial2 );
backMesh2.scale.x = -1;
scene.add( backMesh2 );
backMesh2.visible = false;
function Box() {
this.posn = new THREE.Vector3();
this.rotation = new THREE.Vector3();
this.speed = getRand(3, 20);
this.init();
}
Box.ORIGIN = new THREE.Vector3();
Box.MAX_DISTANCE = 1000;
Box.INIT_POSN_RANGE = 500;
Box.FRONT_PLANE_Z = 1000;
Box.BACK_PLANE_Z = -1000;
Box.prototype.init = function() {
this.posn.copy(Box.ORIGIN);
this.posn.x = getRand(-Box.INIT_POSN_RANGE,Box.INIT_POSN_RANGE);
this.posn.y = getRand(-Box.INIT_POSN_RANGE,Box.INIT_POSN_RANGE);
this.posn.z = Box.BACK_PLANE_Z;
this.rotation.x = (Math.random() * 360 ) * Math.PI / 180;
this.rotation.y = (Math.random() * 360 ) * Math.PI / 180;
this.rotation.z = (Math.random() * 360 ) * Math.PI / 180;
};
Box.prototype.update = function() {
this.posn.z += this.speed * sketchParams.cubeSpeed ;
this.rotation.x += 0.03;
this.rotation.y += 0.01;
if(this.posn.z > Box.FRONT_PLANE_Z) {
this.init();
}
};
// returns random number within a range
function getRand(minVal, maxVal) {
return minVal + (Math.random() * (maxVal - minVal));
}
var cubesize = 100;
var BOX_COUNT;
var geometry = new THREE.CubeGeometry(cubesize, cubesize, cubesize);
cubeHolder = new THREE.Object3D();
THREE.ImageUtils.crossOrigin = '';
imgTextureStripes = THREE.ImageUtils.loadTexture( "./images/stripes2.jpg" );
cubeMaterial = new THREE.MeshPhongMaterial( {
ambient: 0x111111,
color: 0x666666,
specular: 0x999999,
shininess: 30,
shading: THREE.FlatShading,
map:imgTextureStripes
});
for(i = 0; i < BOX_COUNT; i++) {
var box = new Box();
console.log(box);
boxes.push(box);
var cube = new THREE.Mesh(geometry,cubeMaterial );
cube.position = box.posn;
cube.rotation = box.rotation;
cube.ox = cube.scale.x = Math.random() * 1 + 1;
cube.oy = cube.scale.y = Math.random() * 1 + 1;
cube.oz = cube.scale.z = Math.random() * 1 + 1;
cubeHolder.add(cube);
}
scene.add(cubeHolder);
/* ==================== [ Mini Geometries ] ==================== */
/* ==================== [ Post Processing ] ==================== */
composer = new THREE.EffectComposer(renderer);
composer.addPass(new THREE.RenderPass(scene, camera));
effect = new THREE.ShaderPass(THREE.FilmShader);
effect.uniforms['time'].value = 2.0;
effect.uniforms['nIntensity'].value = 0.4;
effect.uniforms['sIntensity'].value = 0.9;
effect.uniforms['sCount'].value = 1800;
effect.uniforms['grayscale'].value = 0.8;
composer.addPass(effect);
// var dot = new THREE.ShaderPass( THREE.DotScreenShader );
// dot.uniforms[ 'scale' ].value = 400;
// dot.uniforms[ 'tDiffuse' ].value = 40;
// dot.uniforms[ 'tSize' ].value = new THREE.Vector2( 256, 256 );
// composer.addPass(dot);
// var kaleidoPass = new THREE.ShaderPass(THREE.KaleidoShader);
// kaleidoPass.uniforms['sides'].value = 3;
// kaleidoPass.uniforms['angle'].value = 45 * Math.PI / 180;
// composer.addPass(kaleidoPass);
// var mirror = mirrorPass = new THREE.ShaderPass( THREE.MirrorShader );
// // mirror.uniforms[ "tDiffuse" ].value = 1.0;
// // mirror.uniforms[ "side" ].value = 3;
// composer.addPass(mirror);
var glitch = new THREE.GlitchPass(64);
glitch.uniforms[ "tDiffuse" ].value = 1.0;
glitch.uniforms[ 'seed' ].value = Math.random() * 5;
glitch.uniforms[ 'byp' ].value = 0;
// glitch.goWild = true;
composer.addPass(glitch);
var superPass = new THREE.ShaderPass(THREE.SuperShader);
superPass.uniforms.vigDarkness.value = 1;
superPass.uniforms.vigOffset.value = 1.3;
superPass.uniforms.glowSize.value = 2;
superPass.uniforms.glowAmount.value = 1;
composer.addPass( superPass );
var tv = new THREE.ShaderPass( THREE.BadTVShader );
tv.uniforms[ "distortion" ].value = 1;
tv.uniforms[ "distortion2" ].value = .01;
// tv.uniforms[ "time" ].value = 1.5;
tv.uniforms[ "speed" ].value = 8.8;
tv.uniforms[ "rollSpeed" ].value = 0.8;
composer.addPass(tv);
var staticPass = new THREE.ShaderPass( THREE.StaticShader );
staticPass.uniforms[ "amount" ].value = 0.15;
staticPass.uniforms[ "size" ].value = 1.0;
staticPass.uniforms[ "time" ].value = 4.5;
composer.addPass(staticPass);
var effect1 = new THREE.ShaderPass(THREE.RGBShiftShader);
effect1.uniforms['amount'].value = 0.003;
effect1.renderToScreen = true;
composer.addPass(effect1);
function turnOnMirror() {
var mirror = mirrorPass = new THREE.ShaderPass( THREE.MirrorShader );
mirror.renderToScreen = true;
composer.addPass(mirror);
}
function turnOffMirror() {
var mirror = mirrorPass = new THREE.ShaderPass( THREE.MirrorShader );
mirror.renderToScreen = false;
composer.addPass(mirror);
}
// add a timer
clock = new THREE.Clock;
/* ==================== [ Stats ] ==================== */
// stats = new Stats();
// stats.domElement.style.position = 'absolute';
// stats.domElement.style.left = '0px';
// stats.domElement.style.top = '0px';
// document.body.appendChild(stats.domElement);
// document.body.appendChild( renderer.domElement );
/* ==================== [ Shapes ] ==================== */
var quantity = 40;
var shapes = [];
for (var i = 0; i < quantity; i++) {
if (Math.random() < 0.5) {
var geometry = new THREE.RingGeometry(4, 40, 3);
// geometry.position = 0;
// var geometry = new THREE.RingGeometry( 30, 30, 18);
// camera.position.z = 60;
// var geometry = new THREE.RingGeometry( 20, 150, 18);
// var geometry = new THREE.RingGeometry( 20, 150, 18);
// var geometry = new THREE.TorusKnotGeometry( 10, 3, 100, 16 );
}
else {
//var geometry = new THREE.RingGeometry( 4, 40, 3);
// var geometry = new THREE.RingGeometry( 30, 30, 18);
// var geometry = new THREE.RingGeometry( 1, 5, 6 );
// var material = new THREE.MeshBasicMaterial( { color: 0xffff00,
// side: THREE.DoubleSide } );
// var mesh = new THREE.Mesh( geometry, material );
// scene.add( mesh );
}
if (i % 7 === 0) {
var material = new THREE.MeshPhongMaterial({
color: "#ffffff"
});
} else if (i % 2 === 0) {
var material = new THREE.MeshPhongMaterial({
color: "#666666"
});
} else {
var material = new THREE.MeshPhongMaterial({
color: "#333333"
});
}
var mesh = new THREE.Mesh(geometry, material);
mesh.position.z = -i * 3;
// mesh.rotation.z = i;
shapes.push(mesh);
scene.add(mesh);
}
// function refRate() {
// curTime = Date.now();
// delta = curTime - oldTime;
//
// if (delta > interval) {
// oldTime = curTime - (delta % interval);
// updateSize();
// }
//
// }
// Variables
var u_time = 0;
/* ==================== [ Render Function ] ==================== */
var render = function () {
requestAnimationFrame(render);
// var timer = Date.now() * 0.0010;
// camera.lookAt(scene.position);
u_time++;
for (var i = 0; i < quantity; i++) {
// Set rotation change of shapes
shapes[i].position.z += 0.2;
shapes[i].rotation.z += 0;
shapes[i].scale.x = 1 + Math.sin(i + u_time * 0.1) * 0.05;
shapes[i].scale.y = 1 + Math.sin(i + u_time * 0.1) * 0.05;
// shapes[i].scale.y = 120 + Math.tan(i + u_time * 5.0) * 0.5;
// shapes[i].scale.x = 120 + Math.tan(i + u_time * 5.0) * 0.5;
var change = 1.5 + Math.sin(u_time * 0.5) * 0.5;
// Set wireframe & width
if (Math.random() < change) {
shapes[i].material.wireframe = false;
shapes[i].material.wireframeLinewidth = Math.random() * 2;
// if (shapes[i] / 2 === 0) {
// turnOnMirror();
// }
}
else {
shapes[i].material.wireframe = false;
}
if (shapes[i].position.z > 10) {
shapes[i].position.z = -70;
shapes[i].rotation.z = i;
}
}
// Set Point light Intensity & Position
pointLight.intensity = Math.abs(Math.sin(u_time * 0.2) * 2);
pointLight2.intensity = Math.abs(Math.cos(u_time * 0.2) * 2);
pointLight.position.z = Math.abs(Math.sin(u_time * 0.02) * 30);
pointLight2.position.z = Math.abs(Math.cos(u_time * 0.02) * 30);
renderer.render(scene, camera);
composer.render();
var pCount = particleCount;
while (pCount--) {
var camz = getCamera().position.z;
var particle = particles.vertices[pCount];
particle.y = Math.random() * 500 - 250;
//particleSystem.vertices[i].z = camz + Math.random()*600 + 200 ;
particleSystem.geometry.vertices.needsUpdate = true;
}
particleSystem.rotation.y += -0.001;
particleSystem.rotation.z += 0.005;
var normLevel = 0.2;
beamGroup.rotation.x += BEAM_ROT_SPEED;
beamGroup.rotation.y += BEAM_ROT_SPEED;
beamMaterial.opacity = Math.min(normLevel * 0.4, 0.6);
camera.rotation.z += 0.003;
if (doShake) {
var maxshake = 60;
var shake = normLevel * maxshake ;
camera.position.x = Math.random()*shake - shake/2;
camera.position.y = Math.random()*shake - shake/2;
}
camera.rotation.z += 0.003;
// camera.rotation.y += 0.005;
// camera.rotation.x -= 0.003;
//camera.rotation.z += 0.03;
if (doStrobe){
strobeOn = !strobeOn;
if (strobeOn){
light2.intensity = 2;
}
else {
light2.intensity = 0.5;
}
}
else {
light2.intensity = 0.2;
}
// flash background on level threshold
if (normLevel > 0.5 ){
renderer.setClearColor ( 0xFFFFFF );
backMesh2.visible = true;
}
else{
renderer.setClearColor ( 0x000000 );
backMesh2.visible = false;
}
// show stripes for 6 frames on beat
backMesh2.visible = beatTime < 6;
for(var i = 0; i < BOX_COUNT; i++) {
boxes[i].update();
}
if (audioSrc.context.currentTime > 32) {
turnOnMirror();
}
// console.log(audioSrc.context.currentTime);
}
render();
I am trying to do like this http://mbostock.github.io/protovis/ex/nbody.html and same project. But my system doesn't work. Can you help me This is my http://mendow.github.io/projects/n-body/index.html
I gues i am doing wrong in place calculating attration each part to each
Problem is particles has one mass center and spin around it instead has mass center which change it position
<!DOCTYPE html>
<html>
<head>
<title>n-body</title>
<script src="http://mendow.github.io/projects/n-body/libs/three.js"></script>
<script src="http://mendow.github.io/projects/n-body/libs/OrbitControls.js"></script>
<script src="http://mendow.github.io/projects/n-body/libs/OBJLoader.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<script>
//define global variable
{
var renderer;
var scene;
var camera;
var orbit;
var ps;
var G = 9.81;
var dt = 0.0001;
var count = 1000;
var cam = 30;
}
function init() {
{
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render, sets the background color and the size
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
// position and point the camera to the center of the scene
camera.position.x = cam;
camera.position.y = cam;
camera.position.z = cam;
camera.lookAt(scene.position);
orbit = new THREE.OrbitControls(camera);
}
setupParticleSystem(count);
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
// call the render function
render();
}
function setupParticleSystem(y) {
var geometry = new THREE.Geometry();
for (var j = 0; j < y; j++) {
var v = new THREE.Vector3();
var ran = 30;
v.x = intRand(ran, -ran);
v.y = intRand(ran, -ran);
v.z = intRand(ran, -ran);
v.vel = new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
v.acc =new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
v.mass = intRand(5, 0);
geometry.vertices.push(v);
}
console.log(geometry.vertices);
// use a material for some styling
var psMat = new THREE.PointCloudMaterial();
psMat.color = new THREE.Color(0x55ff55);
psMat.transparent = true;
psMat.size = 1;
psMat.blending = THREE.AdditiveBlending;
// Create a new particle system based on the provided geometry
ps = new THREE.PointCloud(geometry, psMat);
ps.sizeAttenuation = true;
ps.sortParticles = true;
ps.position.y = 100 / cam;
ps.position.x = 100 / cam;
ps.position.z = 100 / cam;
// add the particle system to the scene
scene.add(ps);
}
var step = 0;
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
var r,
mult;
var geometry = ps.geometry;
var temp = ps.geometry;
for (var i = 0; i < geometry.vertices.length; i++) {
for (var j = 0; j < geometry.vertices.length; j++) {
if (i != j) {
var particle = geometry.vertices[i];
var cntr = geometry.vertices[j];
r = particle.length(cntr);
mult = (-1) * G * (cntr.mass * particle.mass) / Math.pow(r, 3);
particle.acc.x = mult * particle.x;
particle.vel.x += particle.acc.x * dt;
particle.x += particle.vel.x * dt;
particle.acc.y = mult * particle.y;
particle.vel.y += particle.acc.y * dt;
particle.y += particle.vel.y * dt;
particle.acc.z = mult * particle.z;
particle.vel.z += particle.acc.z * dt;
particle.z += particle.vel.z * dt;
}
}
}
geometry.verticesNeedUpdate = true;
geometry.colorsNeedUpdate = true;
orbit.update();
}
// calls the init function when the window is done loading.
window.onload = init;
function mrand() {
return Math.random();
}
function intRand(min, max) {
return Math.random() * (max - min) + min;
}
</script>
<body>
</body>
</html>
When you examine your browser javascript console (F12) you will see this error :
Uncaught SecurityError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The cross-origin image at http://mendow.github.io/projects/n-body/assets/textures/ps_smoke.png may not be loaded.
One solution (see alternative solution below) is to simply put your asset files on same host as your HTML. That is local to your host computer. Here are the steps (linux cmds, amend for windows) :
cd into same dir as your html
mkdir -p assets/textures # create dir to park your ps_smoke.png
cd assets/textures # get into this new dir
# copy that remote file to your local dir
wget http://mendow.github.io/projects/n-body/assets/textures/ps_smoke.png
and then finally update your html
comment out :
psMat.map = THREE.ImageUtils.loadTexture("http://mendow.github.io/projects/n-body/assets/textures/ps_smoke.png");
good new location :
psMat.map = THREE.ImageUtils.loadTexture("assets/textures/ps_smoke.png");
Once I did this your code executes just fine.
Alternative to above solution is to just override this security check by adding following code just prior to the loadTexture call you are making :
THREE.ImageUtils.crossOrigin = '';
Matvey, you need to calculate the changes to all particle locations and velocities with the old values before adding them to get new values. Otherwise you're calculating some of the changes based on altered positions and velocities which is inaccurate.
I've edited your render loop:
<!DOCTYPE html>
<html>
<head>
<title>n-body</title>
<script src="http://mendow.github.io/projects/n-body/libs/three.js"></script>
<script src="http://mendow.github.io/projects/n-body/libs/OrbitControls.js"></script>
<script src="http://mendow.github.io/projects/n-body/libs/OBJLoader.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<script>
//define global variable
{
var renderer;
var scene;
var camera;
var orbit;
var ps;
var G = 9.81;
var dt = 0.0001;
var count = 1000;
var cam = 30;
}
function init() {
{
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render, sets the background color and the size
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
// position and point the camera to the center of the scene
camera.position.x = cam;
camera.position.y = cam;
camera.position.z = cam;
camera.lookAt(scene.position);
orbit = new THREE.OrbitControls(camera);
}
setupParticleSystem(count);
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
// call the render function
render();
}
function setupParticleSystem(y) {
var geometry = new THREE.Geometry();
for (var j = 0; j < y; j++) {
var v = new THREE.Vector3();
var ran = 30;
v.x = intRand(ran, -ran);
v.y = intRand(ran, -ran);
v.z = intRand(ran, -ran);
v.vel = new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
v.acc =new THREE.Vector3(intRand(1, -1), intRand(1, -1), intRand(1, -1));
v.mass = intRand(5, 0);
geometry.vertices.push(v);
}
console.log(geometry.vertices);
// use a material for some styling
var psMat = new THREE.PointCloudMaterial();
psMat.color = new THREE.Color(0x55ff55);
psMat.transparent = true;
psMat.size = 1;
psMat.blending = THREE.AdditiveBlending;
// Create a new particle system based on the provided geometry
ps = new THREE.PointCloud(geometry, psMat);
ps.sizeAttenuation = true;
ps.sortParticles = true;
ps.position.y = 100 / cam;
ps.position.x = 100 / cam;
ps.position.z = 100 / cam;
// add the particle system to the scene
scene.add(ps);
}
var step = 0;
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
var r, mult;
var geometry = ps.geometry;
var temp = ps.geometry;
var dx = [];
var dv = [];
for (var i = 0; i < geometry.vertices.length; i++) {
var v = geometry.vertices[i].vel;
dx.push( new THREE.Vector3( v.x * dt, v.y * dt, v.z * dt ) );
var dvx = 0;
var dvy = 0;
var dvz = 0;
for (var j = 0; j < geometry.vertices.length; j++) {
if (i != j) {
mult = (-1) * G * geometry.vertices[i].mass * geometry.vertices[j].mass;
var vi = geometry.vertices[i];
var vj = geometry.vertices[j];
// http://www.scholarpedia.org/article/N-body_simulations_%28gravitational%29
epsilon = .1;
var r = Math.sqrt( ( vi.x - vj.x ) * ( vi.x - vj.x )
+ ( vi.y - vj.y ) * ( vi.y - vj.y )
+ ( vi.z - vj.z ) * ( vi.z - vj.z ) + epsilon )
dvx += mult * ( vi.x - vj.x ) / Math.pow( r, 3 );
dvy += mult * ( vi.y - vj.y ) / Math.pow( r, 3 );
dvz += mult * ( vi.z - vj.z ) / Math.pow( r, 3 );
}
}
dv.push( new THREE.Vector3( dvx * dt, dvy * dt, dvz * dt ) );
}
for ( var i=0 ; i < geometry.vertices.length ; i++ ) {
geometry.vertices[i].add( dx[i] );
geometry.vertices[i].vel.add( dv[i] );
}
geometry.verticesNeedUpdate = true;
geometry.colorsNeedUpdate = true;
orbit.update();
}
// calls the init function when the window is done loading.
window.onload = init;
function mrand() {
return Math.random();
}
function intRand(min, max) {
return Math.random() * (max - min) + min;
}
</script>
<body>
</body>
</html>
The modification to the denominator of the forces helps to keep energy relatively constant during close encounters of particles as per http://www.scholarpedia.org/article/N-body_simulations_(gravitational)