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>
Related
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);
};
I'm new to THREE.js and with a very poor knowledge in physics - but I am trying to build a football game engine (viewed from top) and right now I'm struggling with the movement of the ball.
when trying to move the ball from side to side, the rotation is always facing one direction and I dont understand how to make this rotate in the direction its moving at.
Ive added a simple code showing this issue. your help is much appreciated.
/*
*
* SET UP MOTION PARAMS
*
*/
var degrees = 10;
var power = 1;
var angleRad = degrees * Math.PI / 120;
var velocityX = Math.cos(angleRad) * power;
var velocityY = Math.sin(angleRad) * power;
var velocityZ = 1;
var friction = 1;
var gravity = 0.2;
var bounciness = 0.9;
window.onload = function (params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
var borders = [40, 24] //indicate where the ball needs to move in mirror position
//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.position.set(100, 1, 0);
light.castShadow = true;
light.position.set(0, 0, 100);
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(5, 5, 5);
var material = new THREE.MeshLambertMaterial({ color: 'gray' });
var ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
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);
var field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
* setting up rotation axis
*/
var rotation_matrix = null;
var setQuaternions = function () {
setMatrix();
ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
}
var setMatrix = function () {
rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
}
setQuaternions();
/*
*
* ANIMATION STEP
*
*/
var render = function (params) {
// add velocity to ball
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
//validate if ball is stop moving
if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
console.log("DONE!");
return;
}
// handle boucing effect
if (ball.position.z < 1) {
velocityZ *= -bounciness;
ball.position.z = 1
}
// Update the object's rotation & apply it
ball.matrix.multiply(rotation_matrix);
ball.rotation.setFromRotationMatrix(ball.matrix);
//reducing speed by friction
angleRad *= friction;
velocityX *= friction;
velocityY *= friction;
velocityZ *= friction;
//set up the matrix
setMatrix();
//validate ball is withing its borders otherwise go in the mirror direction
if (Math.abs(ball.position.x) > borders[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
}
if (Math.abs(ball.position.y) > borders[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
}
// reduce ball height with gravity
velocityZ -= gravity;
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
}
body {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>
<head>
</head>
<body>
</body>
</html>
This is actually a pretty advanced bit of physics to do in a super realistic way if you want to include friction and inertia, etc. But you can take some shortcuts to get a decent visual rolling effect...
If you take the vector in the movement direction of the ball, you can get a perpendicular vector.. by taking the .cross product of the movement vector, with the world up vector.
That vector is the axis that a ball would rotate around if it had complete friction with the ground. Once you have that axis, you can use .rotateOnWorldAxis ( axis : Vector3, angle : Float ) with the object..
then you have to figure out how much to rotate, based on the radius of the ball, and the distance travelled.. so it's the length (called magnitude in my code below) of the movement vector * (PI*2) / the circumference of the ball.
Let me know if this helps...
p.s - Your "angleRad" computation was dividing by 120 instead of 180.. i fixed that.
/*
*
* SET UP MOTION PARAMS
*
*/
var degrees = 35;
var power = 0.45;
var angleRad = degrees * Math.PI / 180;
var velocityX = Math.cos(angleRad) * power;
var velocityY = Math.sin(angleRad) * power;
var velocityZ = 1;
var friction = 1;
var gravity = 0.2;
var bounciness = 0.9;
var ballRadius = 5;
var ballCircumference = Math.PI * ballRadius * 2;
var ballVelocity = new THREE.Vector3();
var ballRotationAxis = new THREE.Vector3(0, 1, 0);
window.onload = function(params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
var borders = [40, 24] //indicate where the ball needs to move in mirror position
//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.position.set(100, 1, 0);
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
});
var ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
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);
var field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
* setting up rotation axis
*/
var rotation_matrix = null;
var setQuaternions = function() {
setMatrix();
ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
}
var setMatrix = function() {
rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
}
setQuaternions();
/*
*
* ANIMATION STEP
*
*/
var render = function(params) {
// add velocity to ball
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
//validate if ball is stop moving
if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
console.log("DONE!");
return;
}
// handle boucing effect
if (ball.position.z < 1) {
velocityZ *= -bounciness;
ball.position.z = 1
}
// Update the object's rotation & apply it
/*
ball.matrix.multiply(rotation_matrix); ball.rotation.setFromRotationMatrix(ball.matrix);
//set up the matrix
setMatrix();
*/
// Figure out the rotation based on the velocity and radius of the ball...
ballVelocity.set(velocityX, velocityY, velocityZ);
ballRotationAxis.set(0, 0, 1).cross(ballVelocity).normalize();
var velocityMag = ballVelocity.length();
var rotationAmount = velocityMag * (Math.PI * 2) / ballCircumference;
ball.rotateOnWorldAxis(ballRotationAxis, rotationAmount)
//reducing speed by friction
angleRad *= friction;
velocityX *= friction;
velocityY *= friction;
velocityZ *= friction;
//validate ball is withing its borders otherwise go in the mirror direction
if (Math.abs(ball.position.x) > borders[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
}
if (Math.abs(ball.position.y) > borders[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
}
// reduce ball height with gravity
velocityZ -= gravity;
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
}
body {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>
<head>
</head>
<body>
</body>
</html>
If I understand your situation correctly, then you'll want to apply a rotation to the ball, that is based around the "right axis" of the ball's local space.
THREE.js provides a number of helper methods to simplify this math, namely the makeRotationAxis() method on the THREE.Matrix4 class.
Conceptually and practically, a minor set of adjustments to your ball.rotation math should achieve what you are wanting. Please see the following code snippet to see how this can be done (or see this working jsFiddle):
/*
*
* SET UP MOTION PARAMS
*
*/
var rotationAngle = 0;
var degrees = 10;
var power = 1;
var angleRad = degrees * Math.PI / 120;
var velocityX = Math.cos(angleRad) * power;
var velocityY = Math.sin(angleRad) * power;
var velocityZ = 1;
var friction = 1;
var gravity = 0.2;
var bounciness = 0.9;
window.onload = function (params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
var borders = [40, 24] //indicate where the ball needs to move in mirror position
//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.position.set(100, 1, 0);
light.castShadow = true;
light.position.set(0, 0, 100);
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(5, 5, 5);
var material = new THREE.MeshLambertMaterial({ color: 'gray' });
var ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
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);
var field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
* setting up rotation axis
*/
var rotation_matrix = null;
var setQuaternions = function () {
setMatrix();
ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
}
var setMatrix = function () {
rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
}
setQuaternions();
/*
*
* ANIMATION STEP
*
*/
var render = function (params) {
// add velocity to ball
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
//validate if ball is stop moving
if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
console.log("DONE!");
return;
}
// handle boucing effect
if (ball.position.z < 1) {
velocityZ *= -bounciness;
ball.position.z = 1
}
// Update the object's rotation & apply it
// ball.matrix.multiply(rotation_matrix);
// Compute the direction vector of the balls current forward direction of motion
var vectorDirection = new THREE.Vector3(velocityX, velocityY, velocityZ);
// Compute the vector about which the balls rotation is calculated. This is at a
// right angle to the vectorDirection, and so we use the cross product to
// calculate this
var axisOfRotation = new THREE.Vector3().crossVectors(vectorDirection, new THREE.Vector3(0,0,1) );
// Normalise the rotation axis to unit length
axisOfRotation.normalize();
// Build a rotation matrix around the rotation axis.
var rotation = new THREE.Matrix4();
rotation .makeRotationAxis(axisOfRotation, rotationAngle)
ball.rotation.setFromRotationMatrix(rotation );
// Decrement the rotation angle to achieve the rolling effect
rotationAngle -= 0.1;
// ball.rotation.setFromRotationMatrix(ball.matrix);
//reducing speed by friction
angleRad *= friction;
velocityX *= friction;
velocityY *= friction;
velocityZ *= friction;
//set up the matrix
setMatrix();
//validate ball is withing its borders otherwise go in the mirror direction
if (Math.abs(ball.position.x) > borders[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
}
if (Math.abs(ball.position.y) > borders[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
}
// reduce ball height with gravity
velocityZ -= gravity;
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
}
body {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>
<head>
</head>
<body>
</body>
</html>
How to change the cycle for drawing points on a sphere THREEjs? Its very slow and i cant see my meshes.
my code below:
let R = 600;
let phi_rad = phi * Math.PI / 180;
let theta_rad = theta * Math.PI / 180;
for (var theta = 0; theta < 180; theta++) {
for (var phi = 0; phi < 360; phi++) {
let random_bm_geometry = new THREE.PlaneGeometry(100,100);
let random_bm_material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
side: THREE.DoubleSide
});
let random_bm_mesh = new THREE.Mesh(random_bm_geometry, random_bm_material);
let x = R * Math.cos(theta_rad) * Math.cos(phi_rad);
let y = R * Math.cos(theta_rad) * Math.sin(phi_rad);
let z = R * Math.sin(theta_rad) * Math.sin(phi_rad);
let sq = 1/Math.sqrt(x*x + y*y + z*z);
random_bm_mesh.position.x = R*x*sq;
random_bm_mesh.position.y = R*y*sq;
random_bm_mesh.position.z = R*z*sq;
scene.add(random_bm_mesh);
random_bm_array.push(random_bm_mesh);
};
};
What shoud i change, because 2 for cycles are weird.
I have a flat image that I want to transform so it looks like a projection on an elliptic cylinder.
I've found the great script from Blindman67 for a regular cylinder at https://stackoverflow.com/a/40999097/4273683
However I don't understand, how to change the script to get an elliptic result. Any idea?
Thanks a lot for help.
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
var canvas = createImage(400,400);
var ctx = canvas.ctx;
document.body.appendChild(canvas)
ctx.clearRect(0,0,500,500)
var image = createImage(400,200);
image.ctx.font = "60px arial";
image.ctx.textAlign = "center";
image.ctx.fillStyle = "#7F5";
image.ctx.fillRect(0,0,image.width,image.height)
image.ctx.fillStyle = "white";
image.ctx.fillText("Wrap around",200,60)
image.ctx.fillText("Some images",200,140)
function draw(ang,tilt, perspective){
var step = 1/(Math.max(image.width,400));
for(var i = 0; i < 1; i += step){
var a = i * Math.PI;
var a1 = (i+ step*2) * Math.PI ;
var ix = i * image.width*1.2;
var iw = step * image.width*1.2;
a += ang * Math.PI * 2;
a1 += ang * Math.PI * 2;
a = Math.PI -a;
a1 = Math.PI -a1;
var x = canvas.width * 0.5;
var y = canvas.height * 0.1;
var x1 = x + Math.cos(a1) * 110;
var y1 = y + Math.sin(a) * tilt;
x += Math.cos(a) * 110;
y += Math.sin(a) * tilt;
var s = Math.sin(a);
var s1 = Math.sin(a1);
if(s > 0 || s1 > 0){
ctx.drawImage(image,ix,0,iw,image.height,x1,y- s * perspective*0.5,x-x1,200 + s * perspective)
}
}
}
var w = canvas.width;
var h = canvas.height;
// main update function
function update(timer){
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.fillStyle = "black"
ctx.fillRect(0,0,w,h);
draw(timer / 2000, 40,30)
requestAnimationFrame(update);
}
requestAnimationFrame(update);
I am creating particles and positioning them randomly with three.js:
for( var i = 0; i < particleCount; i++ ){
var pX = Math.random() * 100-50;
var pY =Math.random() * 100-50;
var pZ = Math.random() * 100-50;
particle = new THREE.Vector3(pX,pY,pZ);
particle.velocity = new THREE.Vector3(Math.random(), Math.random(), pZ);
particles.vertices.push(particle);
}
Then on my requestAnimationFrame update function I am moving the particles:
for (var i = 0; i < particleCount; i++) {
var particle = particles.vertices[i];
particle.y += particle.velocity.y*speed;
particle.x += particle.velocity.x*speed;
}
How can I introduce some limits to the movement ? i.e when the particle reaches the edge of the screen I want to "bounce" them back.
It's better to have direction and velocity for each particle. Direction is always a normalized THREE.Vector3().
Then the code for your particles will be like this:
var particles = [];
var particleCount = 100;
var sizeX = 300;
var sizeY = 200;
var sizeZ = 100;
for (var i = 0; i < particleCount; i++) {
var pX = Math.random() * sizeX - sizeX / 2;
var pY = Math.random() * sizeY - sizeY / 2;
var pZ = Math.random() * sizeZ - sizeZ / 2;
particle = new THREE.Vector3(pX, pY, pZ);
particle.direction = new THREE.Vector3(Math.random() - .5, Math.random() - .5, 0).normalize(); // a normalized vector with random values for x,y
particle.velocity = Math.random() * 50; // speed is 50 units per second
particles.push(particle);
}
Supposing, you use THREE.Points():
var geometry = new THREE.Geometry();
geometry.vertices = particles;
var points = new THREE.Points(geometry, new THREE.PointsMaterial({
size: 5,
color: "red"
}));
scene.add(points);
To set the proper speed (our 50 units per second) we'll need THREE.Clock() and its .getDelta() method:
var clock = new THREE.Clock();
var shift = new THREE.Vector3(); //we will re-use it in the animation loop
var delta = 0; // we will re-use it in the animation loop
And in the animation loop we will do this:
delta = clock.getDelta(); // get period between frames (in seconds)
particles.forEach(function(p) {
if (p.x > sizeX / 2 || p.x < -sizeX / 2) { // it's also can be like if (Math.abs(p.x > sizeX / 2))
p.direction.x = -p.direction.x;
}
if (p.y > sizeY / 2 || p.y < -sizeY / 2) {
p.direction.y = -p.direction.y;
}
if (p.z > sizeZ / 2 || p.z < -sizeZ / 2) {
p.direction.z = -p.direction.z;
}
p.add(shift.copy(p.direction).multiplyScalar(p.velocity * delta)); // here we re-use the `shift` vector
})
points.geometry.verticesNeedUpdate = true; // important, if you won't set it to true you won't get your particles moving
So that's it.
jsfiddle example
PS If you want to use BufferGeometry, then you can refer to this very good SO answer