Hi I am working on a basic breakout/arkanoid game in threeJS. Right now all I have is a paddle and a ball that bounces around the screen. I am trying to get collision working so that when the ball hits the paddle it bounces away. I've been trying to using bounding boxes to accomplish this however I am running into an issue where the .intersect/.intersectsBox are not properly registering an intersection and I don't know why. Below is the code I have so far -
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry(5, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
var cubeBoxHelper = new THREE.BoxHelper(cube, 0xff0000);
var boundingBoxPaddle = new THREE.Box3().setFromObject(cubeBoxHelper);
cubeBoxHelper.update();
const geometrySphere = new THREE.SphereGeometry(1, 32, 32);
const materialSphere = new THREE.MeshBasicMaterial({ color: 0xffff00 });
const sphere = new THREE.Mesh(geometrySphere, materialSphere);
var sphereBoxHelper = new THREE.BoxHelper(sphere, 0xff0000);
var boundingBoxBall = new THREE.Box3().setFromObject(sphereBoxHelper);
sphereBoxHelper.update();
scene.add(cube, cubeBoxHelper, sphere, sphereBoxHelper);
sphere.position.y = 5;
camera.position.z = 15;
camera.position.y = 10;
var xSpeed = 0.0005;
var dx = 0.1;
var dy = 0.1;
function bounce()
{
if (sphere.position.x < -19 || sphere.position.x > 18.5)
{
dx = -dx;
}
if (sphere.position.y < -5 || sphere.position.y > 19)
{
dy = -dy;
}
sphere.position.x += dx;
sphere.position.y += dy;
sphereBoxHelper.update();
}
function intersect()
{
if (boundingBoxBall.intersect(boundingBoxPaddle) == true)
{
console.log("intersection");
}
}
const animate = function ()
{
requestAnimationFrame(animate);
document.addEventListener("keydown", onDocumentKeyDown, false);
function onDocumentKeyDown(event)
{
var keyCode = event.which;
if (keyCode == 65 && cube.position.x >= -18.5)
{
cube.position.x -= xSpeed;
}
else if (keyCode == 68 && cube.position.x <= 18)
{
cube.position.x += xSpeed;
}
cubeBoxHelper.update();
};
bounce();
intersect();
sphereBoxHelper.update();
renderer.render(scene, camera);
};
animate();
Right now I have set it so the intersect function just logs to the console so I can tell what's happening. Any help would be great as I'm not sure what I'm doing wrong.
Box3.intersect
.intersect ( box : Box3 ) : this
box - Box to intersect with.
Computes the intersection of this and box, setting the upper bound of this box to the lesser of the two boxes' upper bounds and the lower bound of this box to the greater of the two boxes' lower bounds. If there's no overlap, makes this box empty.
What this function is actually doing is updating boundingBoxBall with information from boundingBoxPaddle, and possibly even setting boundingBoxBall to be an empty box!
I think the function you're really after is:
Box3.intersectsBox
.intersectsBox ( box : Box3 ) : Boolean
box - Box to check for intersection against.
Determines whether or not this box intersects box.
The intersectsBox function returns a simple true/false, so you can tell if the two boxes have collided.
Also note that your bounding box is relative to the associated geometry. If your transform the Mesh, then you will also need to transform the bounding box. The example code on the Box3 docs actually highlights this:
const box = new THREE.Box3();
// ...
// in the animation loop, compute the current bounding box with the world matrix
box.copy( mesh.geometry.boundingBox ).applyMatrix4( mesh.matrixWorld );
Full Example:
let W = window.innerWidth;
let H = window.innerHeight;
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(28, 1, 1, 1000);
camera.position.set(0, 0, 50);
camera.lookAt(scene.position);
scene.add(camera);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 0, -1);
camera.add(light);
let geo = new THREE.BoxBufferGeometry(5, 5, 5);
geo.computeBoundingBox();
let mat = new THREE.MeshPhongMaterial({
color: "red"
});
const left = new THREE.Mesh(geo, mat);
left.position.set(-15, 0, 0)
scene.add(left);
const right = new THREE.Mesh(geo, mat);
right.position.set(15, 0, 0)
scene.add(right);
geo = new THREE.SphereBufferGeometry(1, 16, 16);
geo.computeBoundingBox();
mat = new THREE.MeshPhongMaterial({
color: "yellow"
});
const ball = new THREE.Mesh(geo, mat);
scene.add(ball);
function render() {
renderer.render(scene, camera);
}
function resize() {
W = window.innerWidth;
H = window.innerHeight;
renderer.setSize(W, H);
camera.aspect = W / H;
camera.updateProjectionMatrix();
render();
}
let rate = 0.1;
let goingRight = true;
let ballBox = new THREE.Box3();
let wallBox = new THREE.Box3();
function animate() {
render();
ball.position.x += ((goingRight) ? 1 : -1) * rate;
ball.updateMatrix();
ball.updateMatrixWorld(true);
ballBox.copy(ball.geometry.boundingBox);
ballBox.applyMatrix4(ball.matrixWorld);
if (goingRight) {
wallBox.copy(right.geometry.boundingBox);
wallBox.applyMatrix4(right.matrixWorld);
if (ballBox.intersectsBox(wallBox)) {
goingRight = false;
}
} else {
wallBox.copy(left.geometry.boundingBox);
wallBox.applyMatrix4(left.matrixWorld);
if (ballBox.intersectsBox(wallBox)) {
goingRight = true;
}
}
requestAnimationFrame(animate);
}
window.addEventListener("resize", resize);
resize();
requestAnimationFrame(animate);
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
background: skyblue;
}
<script src="https://threejs.org/build/three.min.js"></script>
Related
I'd like to calculate and draw the minimum bounding rectangle (MBR) of an object in the image of a camera (2D projection).
The following picture shows the MBR of the cube object of the example provided below.
I took a screenshot of the camera image and drew the MBR (red rectangle) manually by eye.
How can I calculate the MBR (size and position in the camera image) programmatically and draw it in the canvas of the camera image (in real time)?
Edit: I added a MBR to my example based on this related solution, but I drew the MBR on a 2D overlay canvas. It works fine till you move the camera (using the mouse). Then the MBR is not correct (shifted and wrong size). Does the calculation not fit to my example or is there a bug in my implementation?
Note: I do not only want to draw the MBR but also find out the 2D coordinates and size on the camera image.
function computeScreenSpaceBoundingBox(mesh, camera) {
var vertices = mesh.geometry.vertices;
var vertex = new THREE.Vector3();
var min = new THREE.Vector3(1, 1, 1);
var max = new THREE.Vector3(-1, -1, -1);
for (var i = 0; i < vertices.length; i++) {
var vertexWorldCoord = vertex.copy(vertices[i]).applyMatrix4(mesh.matrixWorld);
var vertexScreenSpace = vertexWorldCoord.project(camera);
min.min(vertexScreenSpace);
max.max(vertexScreenSpace);
}
return new THREE.Box2(min, max);
}
function normalizedToPixels(coord, renderWidthPixels, renderHeightPixels) {
var halfScreen = new THREE.Vector2(renderWidthPixels/2, renderHeightPixels/2)
return coord.clone().multiply(halfScreen);
}
const scene = new THREE.Scene();
const light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 1, 1, 0.5 ).normalize();
scene.add( light );
scene.add(new THREE.AmbientLight(0x505050));
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshLambertMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
const renderWidth = window.innerWidth;
const renderHeight = window.innerHeight;
const camera = new THREE.PerspectiveCamera( 75, renderWidth / renderHeight, 0.1, 1000 );
camera.position.z = 10;
const controls = new THREE.OrbitControls( camera );
controls.update();
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const clock = new THREE.Clock();
const overlayCanvas = document.getElementById('overlay');
overlayCanvas.width = renderWidth;
overlayCanvas.height = renderHeight;
const overlayCtx = overlayCanvas.getContext('2d');
overlayCtx.lineWidth = 4;
overlayCtx.strokeStyle = 'red';
const animate = function () {
const time = clock.getElapsedTime() * 0.5;
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
cube.position.x = Math.sin(time) * 5
controls.update();
renderer.render( scene, camera );
const boundingBox2D = computeScreenSpaceBoundingBox(cube, camera);
// Convert normalized screen coordinates [-1, 1] to pixel coordinates:
const {x: w, y: h} = normalizedToPixels(boundingBox2D.getSize(), renderWidth, renderHeight);
const {x, y} =
normalizedToPixels(boundingBox2D.min, renderWidth, renderHeight)
.add(new THREE.Vector2(renderWidth / 2, renderHeight / 2));
overlayCtx.clearRect(0, 0, renderWidth, renderHeight);
overlayCtx.strokeRect(x, y, w, h);
};
animate();
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
#overlay {
position: absolute;
}
<body>
<script src="https://unpkg.com/three#0.104.0/build/three.min.js"></script>
<script src="https://unpkg.com/three#0.104.0/examples/js/Detector.js"></script>
<script src="https://unpkg.com/three#0.104.0/examples/js/controls/OrbitControls.js"></script>
<canvas id="overlay"></canvas>
</body>
Note: Please write a comment if my question is unclear so that I know what to describe in more detail.
Thanks to WestLangley for pointing me to the related solution of Holger L and thanks to manthrax for fixing a bug in that solution that made me miss a bug of my own. In my example, I simply miss-transformed the y-coordinate from WebGL 2D screen coordinates (origin is in the center of the canvas and y-axis points up) to regular 2D canvas coordinates (origin is in the top left corner of the canvas and y-axis points down). I did not consider that the y-axis is reversed. Below is a fixed version of the example:
const computeScreenSpaceBoundingBox = (function () {
const vertex = new THREE.Vector3();
const min = new THREE.Vector3(1, 1, 1);
const max = new THREE.Vector3(-1, -1, -1);
return function computeScreenSpaceBoundingBox(box, mesh, camera) {
box.set(min, max);
const vertices = mesh.geometry.vertices;
const length = vertices.length;
for (let i = 0; i < length; i++) {
const vertexWorldCoord =
vertex.copy(vertices[i]).applyMatrix4(mesh.matrixWorld);
const vertexScreenSpace = vertexWorldCoord.project(camera);
box.min.min(vertexScreenSpace);
box.max.max(vertexScreenSpace);
}
}
})();
const renderWidth = window.innerWidth;
const renderHeight = window.innerHeight;
const renderWidthHalf = renderWidth / 2;
const renderHeightHalf = renderHeight / 2;
const scene = new THREE.Scene();
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 0.5).normalize();
scene.add(light);
scene.add(new THREE.AmbientLight(0x505050));
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshLambertMaterial({color: 0x00ff00});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
const camera = new THREE.PerspectiveCamera(75, renderWidth / renderHeight, 0.1, 1000);
camera.position.z = 10;
const controls = new THREE.OrbitControls(camera);
controls.update();
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const clock = new THREE.Clock();
const overlayCanvas = document.getElementById('overlay');
overlayCanvas.width = renderWidth;
overlayCanvas.height = renderHeight;
const overlayCtx = overlayCanvas.getContext('2d');
overlayCtx.lineWidth = 4;
overlayCtx.strokeStyle = 'red';
const boundingBox2D = new THREE.Box2();
const animate = function () {
const time = clock.getElapsedTime() * 0.5;
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
cube.position.x = Math.sin(time) * 5;
controls.update();
renderer.render(scene, camera);
computeScreenSpaceBoundingBox(boundingBox2D, cube, camera);
// Convert normalized screen coordinates [-1, 1] to pixel coordinates:
const x = (boundingBox2D.min.x + 1) * renderWidthHalf;
const y = (1 - boundingBox2D.max.y) * renderHeightHalf;
const w = (boundingBox2D.max.x - boundingBox2D.min.x) * renderWidthHalf;
const h = (boundingBox2D.max.y - boundingBox2D.min.y) * renderHeightHalf;
overlayCtx.clearRect(0, 0, renderWidth, renderHeight);
overlayCtx.strokeRect(x, y, w, h);
};
animate();
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
#overlay {
position: absolute;
}
<body>
<script src="https://unpkg.com/three#0.104.0/build/three.min.js"></script>
<script src="https://unpkg.com/three#0.104.0/examples/js/Detector.js"></script>
<script src="https://unpkg.com/three#0.104.0/examples/js/controls/OrbitControls.js"></script>
<canvas id="overlay"></canvas>
</body>
I am trying to create spheres and assign them a random color at the vertices of the rectangle (It can be other geometrical from like triangles or hexagons and so forth, for simplicity in this example I want to use a rectangle). http://jsfiddle.net/ElmerCC/ja6zL0k1/
let scene, camera, renderer;
let controls;
let widthWindow = window.innerWidth;
let heightWindow = window.innerHeight;
let aspect = widthWindow / heightWindow;
let mouse = new THREE.Vector2();
let raycaster = new THREE.Raycaster();
let intersect;
let elements = [];
let elementsNew = [];
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 10000);
camera.up.set(0, 0, 1);
camera.position.set(-500, -500, 400);
scene.add(camera);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(widthWindow, heightWindow);
document.body.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
let p = [];
p[0] = new THREE.Vector3(-100, -100, 0);
p[1] = new THREE.Vector3(100, -100, 0);
p[2] = new THREE.Vector3(100, 100, 0);
p[3] = new THREE.Vector3(-100, 100, 0);
//dibujar los nodos
for (let cont = 0; cont < 4; cont++) {
let obj = drawJoint(p[cont], 10, 0x666666, 0, true);
elements.push(obj);
scene.add(obj);
}
var geometry = new THREE.PlaneGeometry(200, 200);
var material = new THREE.MeshBasicMaterial({
color: 0x666666,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(geometry, material);
scene.add(plane);
//document.addEventListener("mousemove", moveMouse);
document.addEventListener("mousedown", downMouse);
}
function downMouse(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
let intersected = raycaster.intersectObjects(elements);
if (intersected.length > 0) {
intersect = intersected[0].object;
let center = intersect.position;
let n = drawJoint(center, 15, Math.random() * 0xffffff, 1, true);
elementsNew.push(n);
scene.add(n);
}
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
controls.update();
renderer.render(scene, camera);
}
function drawJoint(
JtCenter,
JtRadius,
Jtcolor,
JtOpacity,
JtTransparency
) {
let JtMaterial = new THREE.MeshBasicMaterial({
color: Jtcolor,
opacity: JtOpacity,
transparent: JtTransparency
});
let JtGeom = new THREE.SphereGeometry(JtRadius, 10, 10);
let Joint = new THREE.Mesh(JtGeom, JtMaterial);
JtGeom .computeBoundingSphere();
Joint.position.copy(JtCenter);
return Joint;
}
How can I detect the intersection of two sphere objects to avoid overlapping one another?
Spheres are the easiest objects for which you can test intersection.
Note that a Sphere is a mathematical representation, and is different than a Mesh with sphere geometry. (You can still get the mathematical bounding sphere of a Mesh with the boundingSphere property.)
Here is how you'd check if two spheres touch/intersect (you can send this function two boundingSphere properties to check other non-sphere objects).
function spheresIntersect(sphere1, sphere1position, sphere2, sphere2position){
return sphere1position.distanceTo(sphere2position) <= (sphere1.radius + sphere2.radius)
}
I am trying to find the orientation of 3 ordered points in space. I am using the algorithm that I found from this site. https://www.geeksforgeeks.org/orientation-3-ordered-points/
I want to print out the orientation updated on GUI whether it is Clockwise or CounterClockwise when I play with coordinates by slider.
You can view what I did so far in below fiddle.
https://jsfiddle.net/ewLkta45/48/
So to implement this, I added this function.
function findOrientation(){
var Orientation;
var x1=geometry.vertices[0].x;
var y1=geometry.vertices[0].y;
var x2=geometry.vertices[1].x;
var y2=geometry.vertices[1].y;
var x3=geometry.vertices[2].x;
var y3=geometry.vertices[2].y;
Orientation=(y2 - y1)*(x3 - x2) - (y3 - y2)*(x2 - x1);
}
But I do not know how to refresh a text controller. My question is how can I display orientation as CW or CCW on temp controller whenever I move slider?
You can use .listen() of a controller to display changes of its value:
var camera, scene, renderer;
var geometry, material, mesh;
var controller;
var orientation = {
value: 'Sam'
};
init();
animate();
function findOrientation() {
let Orientation = 0;
var x1 = geometry.vertices[0].x;
var y1 = geometry.vertices[0].y;
var x2 = geometry.vertices[1].x;
var y2 = geometry.vertices[1].y;
var x3 = geometry.vertices[2].x;
var y3 = geometry.vertices[2].y;
Orientation = (y2 - y1) * (x3 - x2) - (y3 - y2) * (x2 - x1);
return Orientation;
}
function addDatGui() {
var gui = new dat.GUI();
gui.add(geometry.vertices[0], 'x').name("v1.x").min(-800).max(800).step(5).onChange(onFinishChange);
gui.add(geometry.vertices[0], 'y').name("v1.y").min(-800).max(800).step(5).onChange(onFinishChange);
gui.add(geometry.vertices[1], 'x').name("v2.x").min(-800).max(800).step(5).onChange(onFinishChange);
gui.add(geometry.vertices[1], 'y').name("v2.y").min(-800).max(800).step(5).onChange(onFinishChange);
gui.add(geometry.vertices[2], 'x').name("v3.x").min(-800).max(800).step(5).onChange(onFinishChange);
gui.add(geometry.vertices[2], 'y').name("v3.y").min(-800).max(800).step(5).onChange(onFinishChange);
gui.add(orientation, 'value').name("orientation").listen();
}
function onFinishChange() {
if (findOrientation() < 0) {
orientation.value = 'CW';
} else {
orientation.value = 'CCW';
}
}
function init() {
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1000;
scene = new THREE.Scene();
geometry = new THREE.Geometry();
geometry.vertices = [
new THREE.Vector3(-94, -200, 0),
new THREE.Vector3(92, 68, 0),
new THREE.Vector3(-105, 180, 0)
];
geometry.faces = [new THREE.Face3(0, 1, 2)];
mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: 0xffff00,
side: THREE.DoubleSide,
wireframe: true
}));
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.render(scene, camera);
addDatGui();
}
function animate() {
requestAnimationFrame(animate);
//mesh.rotation.y += 0.09;
mesh.geometry.verticesNeedUpdate = true;
//if(resultOfOrientation<0) text.val='cw';
// else text.val='wc';
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.min.js"></script>
I think dat.GUI is a interface for changing variables not for showing variables, there are many other ways to show the text, e.g. <a>,<p> HTML tag.
You can listen events on dat.GUI controllers:
gui.add(geometry.vertices[0], 'x').name("v1.x").min(-800).max(800).step(5).onChange(function() {
var text = document.getElementById('text');
if (findOrientation() < 0) text.innerHTML = 'The orientation of points : CW';
else text.innerHTML = 'The orientation of points : CCW';
});
Here is my example.
How to i remove the clipped objects that have become transparent or prevent the object below it from being shown.
It would be better if it looks like a real world solid cubes.
This is written in javascript with three.js. HTML and CSS have no faults. Only the rendering shows issue here.
var scene, camera, renderer, cube;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.001;
function init() {
scene = new THREE.Scene();
initLight();
drawScene();
initCamera();
initRenderer();
document.body.appendChild(renderer.domElement);
}
function initLight() {
const light = new THREE.PointLight(0xFFFFFF);
light.position.x = 50;
light.position.y = 50;
light.position.z = 130;
scene.add(light);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
camera.position.set(1, 3, 5);
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.sortObjects = false;
}
function drawScene() {
var material = new THREE.MeshLambertMaterial({ color: 0xFF6600 });
var shape = new THREE.CubeGeometry(1, 1, 1);
cube = new THREE.Group();
for (var a = -10; a <= 10; a = a + 2) {
for (var b = -10; b <= 10; b = b + 2) {
for (var c = -10; c <= 10; c = c + 2) {
var part = new THREE.Mesh(shape, material);
part.position.set(a, b, c);
cube.add(part);
}
}
}
scene.add(cube);
}
function rotateCube() {
cube.rotation.x -= SPEED;
cube.rotation.y -= SPEED;
cube.rotation.z -= SPEED;
}
function render() {
requestAnimationFrame(render);
rotateCube();
renderer.render(scene, camera);
}
init();
render();
<script src="https://threejs.org/build/three.js"></script>
I changed the PerspectiveCamera near plane closer, to 0.01 instead of 1, and added the includes to make your snippet run.
The "transparency" you're seeing are the cubes clipping against the camera near plane. By pulling the plane closer to the camera, you're making the viewport smaller in the world, so it more easily fits in between the cubes.
Another thing is backface culling.. that makes cubes invisible if they are viewed from inside. You can disable backface culling via material.side = THREE.DoubleSide, at the cost of potentially rendering twice as much geometry.
var scene, camera, renderer, cube;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.001;
function init() {
scene = new THREE.Scene();
initLight();
drawScene();
initCamera();
initRenderer();
document.body.appendChild(renderer.domElement);
}
function initLight() {
const light = new THREE.PointLight(0xFFFFFF);
light.position.x = 50;
light.position.y = 50;
light.position.z = 130;
scene.add(light);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 0.01, 10);
camera.position.set(1, 3, 5);
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.sortObjects = false;
}
function drawScene() {
var material = new THREE.MeshLambertMaterial({ color: 0xFF6600, side: THREE.DoubleSide});
var shape = new THREE.CubeGeometry(1, 1, 1);
cube = new THREE.Group();
for (var a = -10; a <= 10; a = a + 2) {
for (var b = -10; b <= 10; b = b + 2) {
for (var c = -10; c <= 10; c = c + 2) {
var part = new THREE.Mesh(shape, material);
part.position.set(a, b, c);
cube.add(part);
}
}
}
scene.add(cube);
}
function rotateCube() {
cube.rotation.x -= SPEED;
cube.rotation.y -= SPEED;
cube.rotation.z -= SPEED;
}
function render() {
requestAnimationFrame(render);
rotateCube();
renderer.render(scene, camera);
}
init();
render();
<script src="https://threejs.org/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
I have to generate a huge number of objects that I can drag individually. Also these objects are limited to a plane form (e.g. rect or circle).
At first I worked with simple CircleGeometries, that are placed inside another geometrie (plane). Also dragging them is very easy but as expected the performance is very very poor for about 200000 of them.
I then decided to use points /particleSystem. The positioning inside a plane works very well but I can't get it managed to make the individual points of the particle system draggable. I found the interactive particles example in the threejs documentation but still have no clou, how to combine them with dragcontrols.
This is my code for creating the particle system and fill a plane with these points:
//Create a plane geometrie, that is later filled with points
var geometry2 = new THREE.CircleGeometry(30,32);
var material2 = new THREE.MeshBasicMaterial( {color: 0x666666, side: THREE.DoubleSide, wireframe:true} );
var mat1 = new THREE.MeshBasicMaterial( {color: 0x00ff00, wireframe:false} );
var plane1 = new THREE.Mesh(geometry2, material2);
geometries.push(plane1); //push to object for draggable elements
scene.add(plane1);
var positionsX;
positionsX = inGeometry.inGeometry(plane1.geometry, 200000); // get positions for points inside plane1
var geometry = new THREE.Geometry();
for (var i = 0; i < positionsX.length; i++) {
geometry.vertices.push(positionsX[i]); //add positions to vertices
}
console.log(geometry);
//Create Particle system
var material = new THREE.PointsMaterial({ size:0.02, color: 0xffffff });
particleSystem = new THREE.Points(geometry, material);
scene.add(particleSystem);
console.log(particleSystem);
var dragGeo = new DragControls(geometries, camera, container); //dragging
Can anybody please help?
Thanks
Just a rough example of how you can drag points:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(1.25, 7, 7);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.PlaneBufferGeometry(10, 10, 10, 10);
geometry.rotateX(-Math.PI * 0.5);
var plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
wireframe: true,
color: "red"
}));
scene.add(plane);
var points = new THREE.Points(geometry, new THREE.PointsMaterial({
size: 0.25,
color: "yellow"
}));
scene.add(points);
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = 0.25;
var mouse = new THREE.Vector2();
var intersects = null;
var plane = new THREE.Plane();
var planeNormal = new THREE.Vector3();
var currentIndex = null;
var planePoint = new THREE.Vector3();
var dragging = false;
window.addEventListener("mousedown", mouseDown, false);
window.addEventListener("mousemove", mouseMove, false);
window.addEventListener("mouseup", mouseUp, false);
function mouseDown(event) {
setRaycaster(event);
getIndex();
dragging = true;
}
function mouseMove(event) {
if (dragging && currentIndex !== null) {
setRaycaster(event);
raycaster.ray.intersectPlane(plane, planePoint);
geometry.attributes.position.setXYZ(currentIndex, planePoint.x, planePoint.y, planePoint.z);
geometry.attributes.position.needsUpdate = true;
}
}
function mouseUp(event) {
dragging = false;
currentIndex = null;
}
function getIndex() {
intersects = raycaster.intersectObject(points);
if (intersects.length === 0) {
currentIndex = null;
return;
}
currentIndex = intersects[0].index;
setPlane(intersects[0].point);
}
function setPlane(point) {
planeNormal.subVectors(camera.position, point).normalize();
plane.setFromNormalAndCoplanarPoint(planeNormal, point);
}
function setRaycaster(event) {
getMouse(event);
raycaster.setFromCamera(mouse, camera);
}
function getMouse(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>