onLoad function to onClick only - javascript

This code causes a particle explosion on my page.
I attached the onClick function to a button in my HTML so it runs when I click that certain button. But when I load the HTML it automatically runs this function and after that only when I click the button.
What I need is that it wont rune when you run the HTML. The function may only run when the button is clicked.
I also would like to ask how I can change te canvas size. Since this function automatically creates a canvas where the function loads in but this one is bigger than the div with the sizes I want it to run in.
//////////////settings/////////
var movementSpeed = 30;
var totalObjects = 500;
var objectSize = 70;
var sizeRandomness = 0;
var color = 0x00BEE0;
/////////////////////////////////
var dirs = [];
var parts = [];
//var container = document.createElement('div');
//document.body.appendChild( container );
var container = document.getElementById('header');
//document.body.appendChild( header );
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000)
camera.position.z = 1000;
var scene = new THREE.Scene();
function ExplodeAnimation(x, y) {
var geometry = new THREE.Geometry();
for (i = 0; i < totalObjects; i++) {
var vertex = new THREE.Vector3();
vertex.x = x;
vertex.y = y;
vertex.z = 0;
geometry.vertices.push(vertex);
dirs.push({
x: (Math.random() * movementSpeed) - (movementSpeed / 2),
y: (Math.random() * movementSpeed) - (movementSpeed / 2),
z: (Math.random() * movementSpeed) - (movementSpeed / 2)
});
}
var material = new THREE.ParticleBasicMaterial({
size: objectSize,
color: color
});
var particles = new THREE.ParticleSystem(geometry, material);
this.object = particles;
this.status = true;
this.xDir = (Math.random() * movementSpeed) - (movementSpeed / 2);
this.yDir = (Math.random() * movementSpeed) - (movementSpeed / 2);
this.zDir = (Math.random() * movementSpeed) - (movementSpeed / 2);
scene.add(this.object);
this.update = function() {
if (this.status == true) {
var pCount = totalObjects;
while (pCount--) {
var particle = this.object.geometry.vertices[pCount]
particle.y += dirs[pCount].y;
particle.x += dirs[pCount].x;
particle.z += dirs[pCount].z;
}
this.object.geometry.verticesNeedUpdate = true;
}
}
}
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
renderer.render(scene, camera);
parts.push(new ExplodeAnimation(0, 0));
render();
function render() {
requestAnimationFrame(render);
var pCount = parts.length;
while (pCount--) {
parts[pCount].update();
}
renderer.render(scene, camera);
}
window.addEventListener('mousedown', onclick, false);
window.addEventListener('resize', onWindowResize, false);
function onClick() {
event.preventDefault();
parts.push(new ExplodeAnimation((Math.random() * sizeRandomness) - (sizeRandomness / 2), (Math.random() * sizeRandomness) - (sizeRandomness / 2)));
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

As I saw you code , I think you should only put your bellow trigger code inside the click button :
renderer.render(scene, camera);
parts.push(new ExplodeAnimation(0, 0));
render();
because it's executed automatically when loading .
See My below Snippet , I ve added a sample button and run() function to show animation on click :
//////////////settings/////////
var movementSpeed = 30;
var totalObjects = 500;
var objectSize = 70;
var sizeRandomness = 0;
var color = 0x00BEE0;
/////////////////////////////////
var dirs = [];
var parts = [];
//var container = document.createElement('div');
//document.body.appendChild( container );
var container = document.getElementById('header');
//document.body.appendChild( header );
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000)
camera.position.z = 1000;
var scene = new THREE.Scene();
function ExplodeAnimation(x, y) {
var geometry = new THREE.Geometry();
for (i = 0; i < totalObjects; i++) {
var vertex = new THREE.Vector3();
vertex.x = x;
vertex.y = y;
vertex.z = 0;
geometry.vertices.push(vertex);
dirs.push({
x: (Math.random() * movementSpeed) - (movementSpeed / 2),
y: (Math.random() * movementSpeed) - (movementSpeed / 2),
z: (Math.random() * movementSpeed) - (movementSpeed / 2)
});
}
var material = new THREE.ParticleBasicMaterial({
size: objectSize,
color: color
});
var particles = new THREE.ParticleSystem(geometry, material);
this.object = particles;
this.status = true;
this.xDir = (Math.random() * movementSpeed) - (movementSpeed / 2);
this.yDir = (Math.random() * movementSpeed) - (movementSpeed / 2);
this.zDir = (Math.random() * movementSpeed) - (movementSpeed / 2);
scene.add(this.object);
this.update = function() {
if (this.status == true) {
var pCount = totalObjects;
while (pCount--) {
var particle = this.object.geometry.vertices[pCount]
particle.y += dirs[pCount].y;
particle.x += dirs[pCount].x;
particle.z += dirs[pCount].z;
}
this.object.geometry.verticesNeedUpdate = true;
}
}
}
var renderer = new THREE.WebGLRenderer();
function render() {
requestAnimationFrame(render);
var pCount = parts.length;
while (pCount--) {
parts[pCount].update();
}
renderer.render(scene, camera);
}
window.addEventListener('mousedown', onclick, false);
window.addEventListener('resize', onWindowResize, false);
function onClick() {
event.preventDefault();
parts.push(new ExplodeAnimation((Math.random() * sizeRandomness) - (sizeRandomness / 2), (Math.random() * sizeRandomness) - (sizeRandomness / 2)));
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
document.getElementById("btn").addEventListener('click',run);
function run() {
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
renderer.render(scene, camera);
parts.push(new ExplodeAnimation(0, 0));
render();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>
<button id="btn">Click to run </button>
<div id="header">

Related

Cannot read properties of undefined (reading 'getHex’) when trying to combine webgl_interactive_cubes with pointer lock three.js

I’m trying to create scene with walk-navigation, with interactive objects, for educational purpose. I’m using Pointer Lock Control example for a walk navigation and interactive cubes example from three.js. It’s beginning so project is far from being perfect, although “walk” works. Unfortunately cursor part bugs out showing
Uncaught TypeError: Cannot read properties of undefined (reading 'getHex')
at render (three-fps.js:200:62)
at animate (three-fps.js:182:3)
this is my code:
import * as THREE from "three";
import { PointerLockControls } from "three/examples/jsm/controls/PointerLockControls.js";
let canvas;
let camera, scene, raycaster, renderer;
let INTERSECTED;
let controls;
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
let canJump = false;
let prevTime = performance.now();
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
const pointer = new THREE.Vector2();
const radius = 100;
init();
animate();
function init() {
canvas = document.getElementById("canvas");
camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
1,
10000
);
camera.position.y = 10;
//controls
controls = new PointerLockControls(camera, canvas);
const onKeyDown = function (event) {
switch (event.code) {
case "ArrowUp":
case "KeyW":
moveForward = true;
break;
case "ArrowLeft":
case "KeyA":
moveLeft = true;
break;
case "ArrowDown":
case "KeyS":
moveBackward = true;
break;
case "ArrowRight":
case "KeyD":
moveRight = true;
break;
case "Space":
if (canJump === true) velocity.y += 350;
canJump = false;
break;
}
};
const onKeyUp = function (event) {
switch (event.code) {
case "ArrowUp":
case "KeyW":
moveForward = false;
break;
case "ArrowLeft":
case "KeyA":
moveLeft = false;
break;
case "ArrowDown":
case "KeyS":
moveBackward = false;
break;
case "ArrowRight":
case "KeyD":
moveRight = false;
break;
}
};
document.addEventListener("keydown", onKeyDown);
document.addEventListener("keyup", onKeyUp);
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1).normalize();
scene.add(light);
const geometry = new THREE.BoxGeometry(20, 20, 20);
for (let i = 0; i < 2000; i++) {
const object = new THREE.Mesh(
geometry,
new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff })
);
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;
scene.add(object);
}
raycaster = new THREE.Raycaster();
// floor
let floorGeometry = new THREE.PlaneGeometry(2000, 2000, 100, 100);
floorGeometry.rotateX(-Math.PI / 2);
const floorMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
scene.add(floor);
// Create a WebGL renderer
renderer = new THREE.WebGLRenderer({ canvas });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
document.addEventListener("mousemove", onPointerMove);
//
window.addEventListener("resize", onWindowResize);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onPointerMove(event) {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
//
function animate() {
requestAnimationFrame(animate);
const time = performance.now();
const delta = (time - prevTime) / 1000;
velocity.x -= velocity.x * 10.0 * delta;
velocity.z -= velocity.z * 10.0 * delta;
direction.z = Number(moveForward) - Number(moveBackward);
direction.x = Number(moveRight) - Number(moveLeft);
direction.normalize(); // this ensures consistent movements in all directions
if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta;
if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta;
controls.moveRight(-velocity.x * delta);
controls.moveForward(-velocity.z * delta);
prevTime = time;
camera.updateMatrixWorld();
render();
}
function render() {
camera.updateMatrixWorld();
// find intersections
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, false);
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);
}
// check for undefined 'emissive' on material
if (INTERSECTED.material.emissive != undefined) {
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex(0xff0000);
}

Redraw three js element

I looked at this example with three js to draw particles with images and works perfectly but i want to change the image with a switch when click a button (calls this function):
const changeImg = function(num) {
switch (num)
{
case 0:
imgData ="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA....";
break;
case 1:
imgData = "data:image/png;base64,iVBORw0KGgoAAAAN..."
break;
}
img.src = imgData;
}
And works but when you click multiple times website becomes slow.
How can I update just the image without slowing down the website?
EDIT 1
I change the code like this:
var renderer, scene, camera, ww, wh, particles, mw, mh, mz, numState;
numState = 0;
mz = 6; // Matrerial size
ww = document.getElementById('map-container').offsetWidth,
wh = 450;
mw = ww * 2;
mh = wh * 2;
var centerVector = new THREE.Vector3(0, 0, 0);
var previousTime = 0
speed = 10
isMouseDown = false;
// Render
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("map"),
antialias: true
});
renderer.setSize(mw, mh);
renderer.setClearColor(0x12347C);
// Scence
scene = new THREE.Scene();
// Camera
camera = new THREE.OrthographicCamera( ww / - 2, ww / 2, wh / 2, wh / - 2, 1, 1000 );
camera.position.set(7, 0, 4);
camera.lookAt(centerVector);
scene.add(camera);
camera.zoom = 4;
camera.updateProjectionMatrix();
// Geometry
var geometry = new THREE.Geometry();
var material = new THREE.PointsMaterial({
size: mz,
color: 0xFFFFFF,
sizeAttenuation: false
});
// Particle
particles = new THREE.Points();
var getImageData = function(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
return ctx.getImageData(0, 0, image.width, image.height);
}
var drawTheMap = function() {
geometry.dispose();
particles.material.dispose();
particles.geometry.dispose();
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
var vertex = new THREE.Vector3();
vertex.x = x - imagedata.width / 2;
vertex.y = -y + imagedata.height / 2;
vertex.z = -Math.random()*500;
vertex.speed = Math.random() / speed + 0.015;
geometry.vertices.push(vertex);
}
}
}
particles.material = material;
particles.geometry = geometry;
scene.add(particles);
requestAnimationFrame(render);
};
var init = function() {
imagedata = getImageData(image);
drawTheMap();
onResize();
window.addEventListener('mousemove', onMousemove, false);
window.addEventListener('mousedown', onMousedown, false);
window.addEventListener('mouseup', onMouseup, false);
window.addEventListener('resize', onResize, false);
};
var onResize = function(){
var mov1, mov2;
ww = document.getElementById('map-container').offsetWidth;
wh = 450;
if (window.innerWidth > 850) {
mw = ww * 2;
mh = wh * 2;
mz = 6;
mov1 = 2.2;
mov2 = 1.9;
particles.material.size = mz;
} else {
mw = ww;
mh = wh;
mz = 3;
mov1 = 2;
mov2 = 2;
particles.material.size = mz;
}
renderer.setSize(mw, mh);
camera.left = ww / - mov1;
camera.right = ww / 2;
camera.top = wh / mov2;
camera.bottom = wh / - 2;
camera.updateProjectionMatrix();
};
var onMouseup = function(){
isMouseDown = false;
}
var onMousedown = function(e){
isMouseDown = true;
lastMousePos = {x:e.clientX, y:e.clientY};
};
var onMousemove = function(e){
if(isMouseDown){
camera.position.x += (e.clientX-lastMousePos.x)/100;
camera.position.y -= (e.clientY-lastMousePos.y)/100;
camera.lookAt(centerVector);
lastMousePos = {x:e.clientX, y:e.clientY};
}
};
var render = function(a) {
requestAnimationFrame(render);
particles.geometry.verticesNeedUpdate = true;
if(!isMouseDown){
camera.position.x += (0-camera.position.x)*0.06;
camera.position.y += (0-camera.position.y)*0.06;
camera.lookAt(centerVector);
}
renderer.render(scene, camera);
};
var imgData;
var image;
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
const changeState = function(state, num) {
document.getElementById('dropbox-choose').innerHTML = state;
numState = num;
switch (numState)
{
case 0:
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
break;
case 1:
imgData = "data:image/png;base64,iVBORw0KGgoI..."
break;
}
image.src = imgData;
}
image = document.createElement("img");
image.onload = init;
image.src = imgData;
And the THREE.WebGLRenderer is only applied once but when I click to change the image, it does not update and also I still have the problem that the website slows down
it's my first time using three js and i don't know if i'm applying well what it says in the documentation
EDIT 2
var renderer, scene, camera, ww, wh, particles, mw, mh, mz, numState;
numState = 0;
mz = 6;
ww = document.getElementById('map-container').offsetWidth,
wh = 450;
mw = ww * 2;
mh = wh * 2;
var centerVector = new THREE.Vector3(0, 0, 0);
var previousTime = 0
speed = 10
isMouseDown = false;
// Render
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("map"),
antialias: true
});
renderer.setSize(mw, mh);
renderer.setClearColor(0x12347C);
// Scence
scene = new THREE.Scene();
// Camera
camera = new THREE.OrthographicCamera( ww / - 2, ww / 2, wh / 2, wh / - 2, 1, 1000 );
camera.position.set(7, 0, 4);
camera.lookAt(centerVector);
scene.add(camera);
camera.zoom = 4;
camera.updateProjectionMatrix();
// Geometry
//var geometry = new THREE.Geometry();
var material = new THREE.PointsMaterial({
size: mz,
color: 0xFFFFFF,
sizeAttenuation: false
});
// Particle
particles = new THREE.Points();
particles.material = material
scene.add(particles);
var getImageData = function(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
return ctx.getImageData(0, 0, image.width, image.height);
}
var drawTheMap = function() {
let vertices = particles.geometry; // this acts as a REFERENCE!
vertices.length = 0; // clears the vertices array
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
var vertex = new THREE.Vector3();
vertex.x = x - imagedata.width / 2;
vertex.y = -y + imagedata.height / 2;
vertex.z = -Math.random()*500;
vertex.speed = Math.random() / speed + 0.015;
vertices.vertices.push(vertex);
}
}
}
particles.geometry.verticesNeedUpdate = true; // Inform three.js of the update
requestAnimationFrame(render);
};
var init = function() {
imagedata = getImageData(image);
drawTheMap();
onResize();
window.addEventListener('mousemove', onMousemove, false);
window.addEventListener('mousedown', onMousedown, false);
window.addEventListener('mouseup', onMouseup, false);
window.addEventListener('resize', onResize, false);
};
var onResize = function(){
var mov1, mov2;
ww = document.getElementById('map-container').offsetWidth;
wh = 450;
if (window.innerWidth > 850) {
mw = ww * 2;
mh = wh * 2;
mz = 6;
mov1 = 2.2;
mov2 = 1.9;
particles.material.size = mz;
} else {
mw = ww;
mh = wh;
mz = 3;
mov1 = 2;
mov2 = 2;
particles.material.size = mz;
}
renderer.setSize(mw, mh);
camera.left = ww / - mov1;
camera.right = ww / 2;
camera.top = wh / mov2;
camera.bottom = wh / - 2;
camera.updateProjectionMatrix();
};
var onMouseup = function(){
isMouseDown = false;
}
var onMousedown = function(e){
isMouseDown = true;
lastMousePos = {x:e.clientX, y:e.clientY};
};
var onMousemove = function(e){
if(isMouseDown){
camera.position.x += (e.clientX-lastMousePos.x)/100;
camera.position.y -= (e.clientY-lastMousePos.y)/100;
camera.lookAt(centerVector);
lastMousePos = {x:e.clientX, y:e.clientY};
}
};
var render = function(a) {
requestAnimationFrame(render);
particles.geometry.verticesNeedUpdate = true;
if(!isMouseDown){
camera.position.x += (0-camera.position.x)*0.06;
camera.position.y += (0-camera.position.y)*0.06;
camera.lookAt(centerVector);
}
renderer.render(scene, camera);
};
var imgData;
var image;
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
const changeState = function(state, num) {
document.getElementById('dropbox-choose').innerHTML = state;
numState = num;
switch (numState)
{
case 0:
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
break;
case 1:
imgData = "data:image/png;base64,iVBORw0KGgoAAA..."
break;
}
image.src = imgData;
}
image = document.createElement("img");
image.onload = init;
image.src = imgData;
When I click to change the image, it does not update and also I still have the problem that the website slows down. I cahaged vertcies.push to vertices.vertices.push()
I know I mentioned disposal in a previous version of my answer, but let's instead consider re-using all of your objects.
particles - Add it to the scene immediately after creation.
material - Assign it to particles immediately; No need to re-assign it every time.
geometry - Don't create it globally, we'll let it work from within particles.
Now what we're going to do is replace the vertices and tell three.js that there are new vertices to upload to the GPU.
var drawTheMap = function() {
let vertices = particles.geometry; // this acts as a REFERENCE!
vertices.length = 0; // clears the vertices array
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
var vertex = new THREE.Vector3();
vertex.x = x - imagedata.width / 2;
vertex.y = -y + imagedata.height / 2;
vertex.z = -Math.random()*500;
vertex.speed = Math.random() / speed + 0.015;
vertices.push(vertex);
}
}
}
particles.geometry.verticesNeedUpdate = true; // Inform three.js of the update
requestAnimationFrame(render);
};
The important part here (other than replacing the contents of the vertices array) is setting particles.geometry.verticesNeedUpdate = true;. This is what triggers three.js to replace the vertices on the GPU. Everything else is re-used, not recreated, so it should run fairly smooth.
The solution is change THREE.geometry to THREE.BufferGeometry
var drawTheMap = function() {
particles.geometry = new THREE.BufferGeometry();
var positions = [];
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
positions.push(x - imagedata.width / 2);
positions.push(-y + imagedata.height / 2);
positions.push(-Math.random()*500);
particles.geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
}
}
}
particles.geometry.verticesNeedUpdate = true;
requestAnimationFrame(render);
};

THREE.js - shooting a ball with a curve ( X / Z axis)

I'm new to THREE.js and with a very poor knowledge in physics but still I want to make a football manager game (played from top view) and I need to know that the kick of the ball is realistic as possible.
I was able to make the ball move and rotate in the correct direction while changing the position of the movement when the ball hits its boundaries.
now I need to deal with a issue of the curve of the ball and how do I make it so the ball with move in an arc to the top and to the sides (X / Y) depending of the angle of the foot hitting the ball
lets just say, I need to know how to handle two scenarios:
1) when kick start from the near bottom axis of the ball
2) when kick start from the near right axis of the ball
your help is highly appropriated. Thank you!
**
- I've added a code showing what i have so far
- I've added an image illustrating my goal (or this person scoring a goal)
/*
*
* SET UP MOTION PARAMS
*
*/
var boundries = [40, 24] //indicate where the ball needs to move in mirror position
var completeFieldDistance = boundries[0] * 2;
var fullPower = 1.8; //the power needed to move the ball the enitre field in one kick
var power = null; //will be set when the kick set in depending on the distance
var isKickStop = false; //indicate the renderer weather to stop the kick
var velocityX = null;
var velocityY = null;
//*** this is where i need help! ***
//how can I make the ball move in the Z axis with a nice curv up depending on a given angle
var curv = 15;
var peak = curv;
var velocityZ = 0;
var friction = 0.98;
var gravity = 0.5;
var bounciness = 0.8;
var minVelocity = 0.035; //for when it need to stop the kick rendering
var ballRadius = 3;
var ballCircumference = Math.PI * ballRadius * 2;
var ballVelocity = new THREE.Vector3();
var ballRotationAxis = new THREE.Vector3(0, 1, 0);
//world meshes
var ball = {};
var field = {};
/*
*
* THE KICK HANDLERS
*
*/
function onKick(angleDeg, distance) {
isKickStop = true;
peak = curv;
power = (distance / completeFieldDistance) * fullPower;
velocityX = Math.cos(angleDeg) * power;
velocityY = Math.sin(angleDeg) * power;
velocityZ = peak / (distance / 2);
requestAnimationFrame(function (params) {
isKickStop = false;
animateKick();
})
}
//** THIS IS WHERE I NEED HELP - how do I make the ball move
// render the movements of the ball
var animateKick = function (params) {
if (isKickStop) { return; }
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
if (Math.abs(velocityX) < minVelocity && Math.abs(velocityY) < minVelocity) {
ball.position.z = ball.bottom;
isKickStop = true;
console.log("DONE!");
return;
}
if (ball.position.z >= peak) {
ball.position.z = peak;
velocityZ *= -1;
}
if (ball.position.z < ball.bottom) {
peak *= gravity;
velocityZ *= -1;
ball.position.z = ball.bottom;
}
// Figure out the rotation based on the velocity and radius of the ball...
ballVelocity.set(velocityX, velocityY, 0);
ballRotationAxis.set(0, 0, 1).cross(ballVelocity).normalize();
var velocityMag = ballVelocity.length();
var rotationAmount = velocityMag * (Math.PI * 2) / ballCircumference;
ball.rotateOnWorldAxis(ballRotationAxis, rotationAmount);
//reduce velocity due to friction
velocityX *= friction;
velocityY *= friction;
//making sure ball is not outside of its boundries
if (Math.abs(ball.position.x) > boundries[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? boundries[0] * -1 : boundries[0];
}
if (Math.abs(ball.position.y) > boundries[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? boundries[1] * -1 : boundries[1];
}
}
window.onload = (function (params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
//set the scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xeaeaea);
//set the camera
var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
camera.position.z = 120;
//set the light
var light = new THREE.SpotLight(0xffffff, 1);
light.castShadow = true;
light.position.set(0, 0, 35);
scene.add(light);
// set the renderer
var renderer = new THREE.WebGLRenderer();
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
/*
*
* ADD MESH TO SCENE
*
*/
// create and add the ball
var geometry = new THREE.SphereGeometry(ballRadius, 8, 8);
//make a checkerboard texture for the ball...
var canv = document.createElement('canvas')
canv.width = canv.height = 256;
var ctx = canv.getContext('2d')
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = 'black';
for (var y = 0; y < 16; y++)
for (var x = 0; x < 16; x++)
if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
var ballTex = new THREE.Texture(canv);
ballTex.needsUpdate = true;
var material = new THREE.MeshLambertMaterial({
map: ballTex
});
ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
ball.bottom = ballRadius / 2;
scene.add(ball);
// create and add the field
var margin = 20;
var fieldRatio = 105 / 68;
var width = 90;
var height = width / fieldRatio;
var material = new THREE.MeshLambertMaterial({ color: 'green' });
var geometry = new THREE.BoxGeometry(width, height, 1);
field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
*
* HANDLING EVENTS
*
*/
var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
domEvents.addEventListener(field, 'click', function (e) {
//set points 1 and 2
var p1 = { x: e.intersect.point.x, y: e.intersect.point.y };
var p2 = { x: ball.position.x, y: ball.position.y };
var angleDeg = Math.atan2(p1.y - p2.y, p1.x - p2.x);
var a = p1.x - p2.x;
var b = p1.y - p2.y;
var distance = Math.sqrt(a * a + b * b);
window.onKick(angleDeg, distance);
}, false);
/*
*
* ANIMATION STEP
*
*/
var render = function (params) {
//render kick if it is on the go
if(!isKickStop){
animateKick();
}
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
})()
body {
padding: 0;
margin: 0;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://www.klika.co.il/scripts/three.events.js"></script>
</head>
<body>
</body>
</html>
I build a model to mock this, the model accept several parameters, initial velocity and angular velocity, there are three force on the ball, gravity, air resistance force and Magnus force.
v0_x = 0; //initial velocity
v0_y = 4;
v0_z = 1;
w_x = 0 * Math.PI; // initial angular velocity
w_y = 2 * Math.PI;
w_z = 0 * Math.PI;
m = 2; //weight
rho = 1.2; // air density
g = 9.8; // gravity
f = 10; //frequency of the rotation of the ball
cl = 1.23; //horizontal tension coefficient
cd = 0.5; //air resistance coefficient
D = 0.22; // diameter of the ball
A = Math.PI * Math.pow((0.5 * D), 2); //cross-sectional area of the ball
t_step = 1 / 60;
b = (1 / 2) * cd * rho * A; //for convenience
c = cl * rho * Math.pow(D, 3) * f; // for convenience
vt_x = v0_x
vt_y = v0_y
vt_z = v0_z
animateKick = function() {
if (ball.position.y < 0) {
return;
}
tmp_1 = c * Math.pow(Math.pow(vt_x, 2) + Math.pow(vt_z, 2) + Math.pow(vt_y, 2), 2)
tmp_2 = (Math.sqrt(Math.pow(w_z * vt_y - w_y * vt_z, 2) + Math.pow(w_y * vt_x - w_x * vt_y, 2) + Math.pow(w_x * vt_z - w_z * vt_x, 2)))
tmp = tmp_1 / tmp_2
Fl_x = tmp * (w_z * vt_y - w_y * vt_z)
Fl_z = tmp * (w_y * vt_x - w_x * vt_y)
Fl_y = tmp * (w_x * vt_z - w_z * vt_y)
//Motion differential equation
a_x = -(b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_x + (Fl_x / m)
a_z = -(b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_z + (Fl_z / m)
a_y = -g - (b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_y + (Fl_y / m)
//use formula : s_t = s_0 + v_0 * t to update the position
ball.position.x = ball.position.x + vt_x * t_step
ball.position.z = ball.position.z + vt_z * t_step
ball.position.y = ball.position.y + vt_y * t_step
//use formula : v_t = a * t to update the velocity
vt_x = vt_x + a_x * t_step
vt_z = vt_z + a_z * t_step
vt_y = vt_y + a_y * t_step
}
window.onload = (function() {
gWidth = window.innerWidth;
gHeight = window.innerHeight;
ratio = gWidth / gHeight;
scene = new THREE.Scene();
scene.background = new THREE.Color(0xeaeaea);
camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
camera.position.z = -15;
light = new THREE.SpotLight(0xffffff, 1);
light.castShadow = true;
light.position.set(0, 5, -10);
scene.add(light);
renderer = new THREE.WebGLRenderer();
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(D, 8, 8);
//make a checkerboard texture for the ball...
canv = document.createElement('canvas')
canv.width = canv.height = 256;
ctx = canv.getContext('2d')
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = 'black';
for (y = 0; y < 16; y++)
for (x = 0; x < 16; x++)
if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
ballTex = new THREE.Texture(canv);
ballTex.needsUpdate = true;
material = new THREE.MeshLambertMaterial({
map: ballTex
});
ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
ball.bottom = D / 2;
scene.add(ball);
camera.lookAt(ball.position);
plane_geometry = new THREE.PlaneGeometry(20, 100, 32);
plane_material = new THREE.MeshBasicMaterial({
color: 'green',
side: THREE.DoubleSide
});
ground_plane = new THREE.Mesh(plane_geometry, plane_material);
ground_plane.rotation.x = 0.5 * Math.PI
ground_plane.position.y = -1
ground_plane.position.z = 20
scene.add(ground_plane);
render = function(params) {
animateKick();
renderer.render(scene, camera);
requestAnimationFrame(render);
};
render();
})
body {
padding: 0;
margin: 0;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://www.klika.co.il/scripts/three.events.js"></script>
</head>
<body>
</body>
</html>

Bad FPS in a simple Three.js scene in Safari MacOS

I have a simple demo that works fine in Chrome and Firefox. But Safari is lag like hell. If I disable antialising, it increases the FPS but not that much. How can i optimize my code ? Maybe use some caching? I'm new to three.js
It will be nice if it will work in iOs safari too.
Here's the demo:
var $container = $('#torus');
var mouseX = 0,
mouseY = 0;
var camera, scene, renderer;
var geometry, material, mesh, config;
var numTorus = 65;
var tabTorus = [];
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
config = {
speed: 1.3,
rotation: 1,
opacity: 1
}
function init() {
camera = new THREE.PerspectiveCamera(100, window.innerWidth / window.innerHeight, 10, 1000);
camera.position.z = 1;
scene = new THREE.Scene();
material = new THREE.MeshNormalMaterial();
material.transparent = true;
material.opacity = config.opacity;
geometry = new THREE.TorusGeometry(130, .5, 120, 100);
geometry.computeFaceNormals();
geometry.computeVertexNormals();
geometry.normalsNeedUpdate = true;
geometry.verticesNeedUpdate = true;
geometry.dynamic = true;
for (var i = 0; i < numTorus; i++) {
tabTorus.push(new Torus(-i * 6));
scene.add(tabTorus[i].b);
}
renderer = new THREE.WebGLRenderer({
antialias: true,
autoClear: true,
alpha: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
$container.append(renderer.domElement);
}
function Torus(f) {
this.b = new THREE.Mesh(geometry, material);
this.b.position.y = 55 * Math.sin(f);
this.b.position.x = 55 * Math.cos(f);
this.b.position.z = f * 2.72;
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 10;
mouseY = (event.clientY - windowHalfY) / 10;
}
function update() {
for (var i = 0; i < numTorus; i++) {
tabTorus[i].b.position.z += config.speed * 1.5;
tabTorus[i].b.material.opacity = config.opacity;
tabTorus[i].b.geometry.parameters.arc = 0.5 + config.opacity * 10;
if (tabTorus[i].b.position.z > 0) {
tabTorus[i].b.position.z = -1000;
}
}
}
function animate() {
requestAnimationFrame(animate);
camera.position.x += (mouseX - camera.position.x) * .02;
camera.position.y += (-mouseY - camera.position.y) * .02;
renderer.render(scene, camera);
update();
}
window.addEventListener('resize', onWindowResize, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
init();
animate();
body {
background: black;
}
#torus {
position: fixed;
width: 100%;
height: 100%;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
<div id="torus"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>
It seems to be a problem in Safari itself: enormous amounts of time spent compositing the page (40-50ms on my machine vs 2-4 ms for JavaScript). Also, it depends on size of the window. You should report it to https://bugs.webkit.org.

three.js n-body simulation

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)

Categories