How to get corners of shapes in threejs - three.js

I am developing a shape drawing tool in threejs. I have drawn line, square, circle, rectangle, triangle, ellipse etc. on mouse down + drag successfully. Now i want editing of these shapes, such as scale, move and rotate. How do I get the corners and put points at corners where user can mouse+drag to scale , move points etc.
function onMouseMove(event) {
if(edit) {
event.preventDefault();
if ( selectedObject ) {
selectedObject.material.color.set( '#49BFFE' );
selectedObject = null;
}
var intersects = getIntersects( event );
if ( intersects.length > 0 ) {
var res = intersects.filter( function ( res ) {
return res && res.object;
} )[ 0 ];
if ( res && res.object ) {
selectedObject = res.object;
selectedObject.material.color.set( '#f00' );
console.log(selectedObject)
}
}
}
}
Code to create shapes is as follows:
this.onMouseDown = function( relative ) {
var geometry = new THREE.PlaneBufferGeometry( 1, 1);
var material = new THREE.MeshBasicMaterial({ color: 0x49BFFE, wireframe: false });
square = new THREE.Mesh(geometry, material);
scope.group.add(square);
}
this.onDrag = function( clientX, clientY ) {
var scale = Math.abs(clientX) + Math.abs(clientY);
if(scale == 0) scale = scale + 0.1;
square.scale.set(scale, scale, 0.1);
}

Related

GLB animation in three.js is too fast

I have uploaded a glb file with an animation, and the animation is moving extremely fast, and I do not know why.
This is my character's animation code:
class MainChar extends THREE.Object3D {
constructor() {
super();
this.object = new THREE.Object3D();
this.object.position.set(0, 1, 50);
this.object.scale.x=20;
this.object.scale.y=20;
this.object.scale.z=20;
//load house model form blender file
/*
loader.setPath('../../models/characters/');
const gltf = loader.load('Douglas.glb', (gltf) => {
gltf.scene.traverse(c => {
c.castShadow = true;
});
this.object.add( gltf.scene);
});
*/
const loader = new THREE.GLTFLoader();
loader.setPath('../../models/characters/');
const gltf = loader.load('walk.glb', (gltf) => {
gltf.scene.traverse(c => {
c.castShadow = true;
});
this.mixer = new THREE.AnimationMixer( gltf.scene );
this.mixer.timeScale=1/5;
var action = this.mixer.clipAction( gltf.animations[ 0 ] );
action.play();
this.object.add( gltf.scene );
});
//save keyboard bindings
this.keyboard = new THREEx.KeyboardState();
/*
//creating a box (need to change it to a character with animations)
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
this.object = new THREE.Mesh( geometry, material );
this.object.scale.x=5;
this.object.scale.y=10;
this.object.scale.z=5;
//starting position for character
this.object.position.set(0, 10, 50);
*/
this.update = function (time) {
if ( this.mixer ){
this.mixer.update( time );
console.log(time);
}
//MOVEMENT OF BOX
//speed
var moveDistance = 0.5 ;
// var rotateAngle = Math.PI / 2 * 0.05;
// move forwards/backwards/left/right
if ( this.keyboard.pressed("W") ){
this.object.translateZ( -moveDistance );
}
if ( this.keyboard.pressed("S") ){
this.object.translateZ( moveDistance );
}
if ( this.keyboard.pressed("A") ){
this.object.translateX( -moveDistance );
}
if ( this.keyboard.pressed("D") ){
this.object.translateX( moveDistance );
}
// move forwards/backwards/left/right
if ( this.keyboard.pressed("up") ){
this.object.translateZ( -moveDistance );
}
if ( this.keyboard.pressed("down") ){
this.object.translateZ( moveDistance );
}
if ( this.keyboard.pressed("left") ){
this.object.translateX( -moveDistance );
}
if ( this.keyboard.pressed("right") ){
this.object.translateX( moveDistance );
}
// FOR CAMERA ROTATIONS
//this.object.rotateOnAxis( new THREE.Vector3(0,1,0), -rotateAngle);
//this.object.rotateOnAxis( new THREE.Vector3(0,1,0), rotateAngle);
//var rotation_matrix = new THREE.Matrix4().identity();
if ( this.keyboard.pressed("Z") )
{
this.object.position.set(0, 1, 50);
this.object.rotation.set(0,0,0);
}
/*
// global coordinates
if ( this.keyboard.pressed("left") )
this.object.position.x -= moveDistance;
if ( this.keyboard.pressed("right") )
this.object.position.x += moveDistance;
if ( this.keyboard.pressed("up") )
this.object.position.z -= moveDistance;
if ( this.keyboard.pressed("down") )
this.object.position.z += moveDistance;
*/
};
}
}
This is the time class that allows the game to be paused, as well as returns the delta time:
class Time {
constructor(){
this.is_pause = false;
this.accumalated_run_time = 0;
this.clock = new THREE.Clock();
this.pause_clock = new THREE.Clock();
}
getRunTime()
{
this.accumalated_run_time += this.clock.getDelta();
return this.accumalated_run_time
}
pause()
{
this.is_pause = true;
}
unpause()
{
this.is_pause = false;
this.clock.getDelta();
}
}
This is the sceneManager that calls up my character for updating animations:
class SceneManager {
constructor(canvas) {
//this entire function renders a scene where you can add as many items as you want to it (e.g. we can create the house and add as
//many items as we want to the house). It renders objects from other javascript files
//------------------------------------------------------------------------------------------------------------------------------------------
//These are supposed to act like constants. DO NOT CHANGE
this.GAME_PAUSE = "pause";
this.GAME_RUN = "run";
//------------------------------------------------------------------------------------------------------------------------------------------
//we use (this) to make variables accessible in other classes
this.time = new Time();
this.game_state = this.GAME_RUN;
this.screenDimensions = {
width: canvas.width,
height: canvas.height
};
//the essentials for rendering a scene
this.scene = this.buildScene();
this.renderer = this.buildRender(this.screenDimensions);
this.camera = this.buildCamera(this.screenDimensions);
this.managers = this.createManagers();
this.loadToScene(this.managers[0].entities);
//allow camera to orbit target (player)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target.set(0, 20, 0);
this.controls.update();
}
loadToScene(entities)
{
for (let i = 0 ; i < entities.length ; i++)
{
console.log("before" +i.toString());
this.scene.add(entities[i].object);
console.log("after");
}
}
//this function creates our scene
buildScene() {
//create a new scene
const scene = new THREE.Scene();
//set the scene's background-> in this case it is our skybox
const loader = new THREE.CubeTextureLoader();
//it uses different textures per face of cube
const texture = loader.load([
'../skybox/House/posx.jpg',
'../skybox/House/negx.jpg',
'../skybox/House/posy.jpg',
'../skybox/House/negy.jpg',
'../skybox/House/posz.jpg',
'../skybox/House/negz.jpg'
]);
scene.background = texture;
//if we wanted it to be a colour, it would have been this commented code:
//scene.background = new THREE.Color("#000");
return scene;
}
//this creates a renderer for us
buildRender({ width, height }) {
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, alpha: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
return renderer;
}
//create a camera for the screen
buildCamera({ width, height }) {
//SETTING FIELD OF VIEW, ASPECT RATIO (which should generally be width/ height), NEAR AND FAR (anything outside near/ far is clipped)
const aspectRatio = width / height;
const fieldOfView = 60;
const nearPlane = 1;
const farPlane = 1000;
//there are 2 types of cameras: orthographic and perspective- we will use perspective (more realistic)
const camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, nearPlane, farPlane);
//set where the camera is
camera.position.set(-50, 50, 70);
return camera;
}
//add subjects to the scene
createManagers() {
const managers=[new EntityManager()];
//can be altered so we can add multiple entities, and depending on which position
//it is, certain ones won't be paused, and some will be
managers[0].register(new GeneralLights());
managers[0].register(new House());
managers[0].register(new MainChar());
managers[0].register(new SceneSubject())
return managers;
}
//this updates the subject/model every frame
update() {
//won't call this loop if it's paused-> only for objects that need to be paused (managers that need to be paused)
if (this.game_state == this.GAME_RUN)
{
const runTime = this.time.getRunTime();
this.managers[0].update(runTime);
}
//update orbit controls
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
//this resizes our game when screen size changed
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
pause(){ //when pause mode is entered. The pause menu needs to be rendered.
this.game_state = this.GAME_PAUSE;
this.time.pause();
}
unpause(){
this.game_state = this.GAME_RUN;
this.time.unpause();
}
}
I think the issue is with your AnimationMixer.update() call. If you look at the docs, update is expecting a time-delta in seconds, but it looks like you're passing the total running time. This means it should receive the time passed since the last frame. You can fix this by using clock.getDelta(); as the argument:
this.update = function (time) {
if ( this.mixer ){
const delta = this.clock.getDelta();
this.mixer.update(delta);
}
// ...
}

Three.js - Smooth shading results in weird edges

I’m trying to get .stl files to appear smooth, but the edges result in these weird dark areas.
With flatShading set to true
With flatShading set to false
Is there any way to make the edges perfectly smooth without these weird artifacts?
var renderer = new THREE.WebGLRenderer({ alpha: true });
var camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 500);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', function () {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}, false);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.2;
var scene = new THREE.Scene();
// Hemisphere light
var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 100, 0);
scene.add(hemiLight);
// Directional light
var dirLight = new THREE.DirectionalLight(0x323232);
dirLight.position.set(- 0, 40, 50);
dirLight.castShadow = true;
dirLight.shadow.camera.top = 50;
dirLight.shadow.camera.bottom = - 25;
dirLight.shadow.camera.left = - 25;
dirLight.shadow.camera.right = 25;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 200;
dirLight.shadow.mapSize.set(1024, 1024);
scene.add(dirLight);
var loader = new THREE.STLLoader();
loader.load( 'https://bymu.eu/test.stl', function ( geometry ) {
var material = new THREE.MeshPhongMaterial({ specular: 0x111111, shininess: 200, color: 0xff5533, flatShading: false });
var tempGeometry = new THREE.Geometry().fromBufferGeometry(geometry);
tempGeometry.mergeVertices();
tempGeometry.computeVertexNormals();
tempGeometry.computeFaceNormals();
geometry.fromGeometry(tempGeometry);
var mesh = new THREE.Mesh(tempGeometry, material);
scene.add(mesh);
// Compute the middle
var middle = new THREE.Vector3();
geometry.computeBoundingBox();
geometry.boundingBox.getCenter(middle);
// Center it
mesh.position.x = -1 * middle.x;
mesh.position.y = -1 * middle.y;
mesh.position.z = -1 * middle.z;
// Pull the camera away as needed
var largestDimension = Math.max(geometry.boundingBox.max.x,
geometry.boundingBox.max.y, geometry.boundingBox.max.z)
camera.position.z = largestDimension * 1.5;
render();
});
function render() {
renderer.render( scene, camera );
}
var animate = function () {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}; animate();
body {
background: #b2b2b2;
margin:0;
padding:0;
overflow: hidden;
}
<script src="https://raw.githack.com/mrdoob/three.js/dev/build/three.min.js"></script>
<script src="https://raw.githack.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
<script src="https://raw.githack.com/mrdoob/three.js/dev/examples/js/loaders/STLLoader.js"></script>
The problem isn't with Three.js, but with your geometry. Three.js uses "vertex normals" to know which direction the vertex is facing. This is used to smooth out faces. See the illustration below, your edge has a "smooth edge" (left diagram), where the direction of the faces is blended along that 90-degree angle. If you want a "sharp edge" (on the right), you'll have to tell your your geometry to create a second normal, each one pointing perpendicular to the face, so the angles don't blend.
.
Here's what your normals look like in Blender, in a before/after animation. Notice that a single normal down the middle gives the undesired smooth shading:
The way to achieve this varies from one editor to another, but I'm sure you can find the exact step-by-step instructions by looking up "mark sharp edge" for your editor of choice.
Solved it, found this great function https://codepen.io/Ni55aN/pen/zROmoe
THREE.Geometry.prototype.computeAngleVertexNormals = function(angle){
function weightedNormal( normals, vector ) {
var normal = new THREE.Vector3();
for ( var i = 0, l = normals.length; i < l; i ++ ) {
if ( normals[ i ].angleTo( vector ) < angle ) {
normal.add( normals[ i ] );
}
}
return normal.normalize();
}
this.computeFaceNormals();
var vertexNormals = [];
for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
vertexNormals[ i ] = [];
}
for ( var i = 0, fl = this.faces.length; i < fl; i ++ ) {
var face = this.faces[ i ];
vertexNormals[ face.a ].push( face.normal );
vertexNormals[ face.b ].push( face.normal );
vertexNormals[ face.c ].push( face.normal );
}
for ( var i = 0, fl = this.faces.length; i < fl; i ++ ) {
var face = this.faces[ i ];
face.vertexNormals[ 0 ] = weightedNormal( vertexNormals[ face.a ], face.normal );
face.vertexNormals[ 1 ] = weightedNormal( vertexNormals[ face.b ], face.normal );
face.vertexNormals[ 2 ] = weightedNormal( vertexNormals[ face.c ], face.normal );
}
if ( this.faces.length > 0 ) {
this.normalsNeedUpdate = true;
}
}
Results in exactly what I want after playing around with the angle, however increases loading time and has a bit of a performance penalty when a bunch of meshes are loaded, something I can live with as the meshes look amazing. I tried exporting the meshes so the browser wouldn’t have to recalculate every time, but some of them inflated 5-10 times due to this process. So it’s a sacrifice of loading time either way.

three.js : extrude with holes issue

I try to make a shape with holes and to extrude it, but I got a strange result :
here is my code :
var shape = new THREE.Shape();
shape.moveTo(0,0);
var radius = 1;
shape.absarc(0,radius,radius,3/2*(Math.PI),1/2*(Math.PI), false);
shape.lineTo(6.34,ToolSize);
shape.absarc(6.34,radius,radius,1/2*(Math.PI),3/2*(Math.PI), false);
for (var i=0; i<3; i++) {
var hole = new THREE.Shape();
var centerX = 1.9+i*1.27;
hole.absarc(centerX,0.6,0.3,0,2*(Math.PI), false);
shape.holes.push(hole);
}
var extrudeSettings={amount: 2, bevelEnabled: false, material: 0, extrudeMaterial: 1, steps: 10};
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var material1 = new THREE.MeshStandardMaterial({color: 0x111111, roughness: 0.1, metalness: 0.4, side: THREE.DoubleSide});
var material2 = new THREE.MeshStandardMaterial({color: 0x8dbe8d, roughness: 0.7, metalness: 0, side: THREE.DoubleSide});
var materials = [
material1,
material2];
var mesh = THREE.SceneUtils.createMultiMaterialObject(geometry,materials);
Thank you for your help
The triangulation will work if you use earcut as the triangulation algorithm.
This change should be made permanent in future releases of three.js, but for now, follow the changes implemented in this three.js example.
The change involves adding the following code:
<!-- replace built-in triangulation with Earcut -->
<script src="js/libs/earcut.js"></script>
<script>
THREE.ShapeUtils.triangulateShape = function ( contour, holes ) {
function removeDupEndPts( points ) {
var l = points.length;
if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {
points.pop();
}
}
function addContour( vertices, contour ) {
for ( var i = 0; i < contour.length; i ++ ) {
vertices.push( contour[ i ].x );
vertices.push( contour[ i ].y );
}
}
removeDupEndPts( contour );
holes.forEach( removeDupEndPts );
var vertices = [];
addContour( vertices, contour );
var holeIndices = [];
var holeIndex = contour.length;
for ( i = 0; i < holes.length; i ++ ) {
holeIndices.push( holeIndex );
holeIndex += holes[ i ].length;
addContour( vertices, holes[ i ] );
}
var result = earcut( vertices, holeIndices, 2 );
var grouped = [];
for ( var i = 0; i < result.length; i += 3 ) {
grouped.push( result.slice( i, i + 3 ) );
}
return grouped;
};
</script>
three.js r.88

Why does this cube not rotate around its own axes?

I've got a cube which I would like to rotate around its own axes in 3D space by using keyboard input. The cube is still rotating around the world axes.
Here's my code:
var rotation_matrix_y, rotation_matrix_x, rotation_matrix_z;
var geometry = new THREE.CubeGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00, wireframe: true});
var cube = new THREE.Mesh(geometry, material);
var axisHelper = new THREE.AxisHelper( 5 );
cube.add( axisHelper );
scene.add(cube);
cube.rotation.set(0, 0, 0);
cube.matrix.makeRotationFromEuler(cube.rotation);
var render = function () {
document.addEventListener("keydown", onKeyDown, false);
function onKeyDown(e) {
// W - up
if (e.keyCode == 87) {
rotation_matrix_x = new THREE.Matrix4().makeRotationX(.0001);
cube.applyMatrix(rotation_matrix_x);
}
// S - down
if (e.keyCode == 83) {
rotation_matrix_x = new THREE.Matrix4().makeRotationX(-0.0001);
cube.applyMatrix(rotation_matrix_x);
}
// D - right
if (e.keyCode == 68) {
rotation_matrix_y = new THREE.Matrix4().makeRotationY(-0.0001);
cube.applyMatrix(rotation_matrix_y);
}
// A - left
if (e.keyCode == 65) {
rotation_matrix_y = new THREE.Matrix4().makeRotationY(0.0001);
cube.applyMatrix(rotation_matrix_y);
}
requestAnimationFrame(render);
stats.update();
renderer.render(scene, camera);
};
There are a couple of questions similar to this one, but they all seem to be outdated and some of the methods have been deprecated.
An easy way to rotate an object on its own x-axis is like so:
object.rotateX( radians );
There is also object.rotateY( radians ); and object.rotateZ( radians );
three.js r.66

Threejs - How to pick all objects in area?

I'm using Three.js and I wonder how to get all objects in a given area?
For example, get all objects that found in the green-square:
Solution:
getEntitiesInSelection: function(x, z, width, height, inGroup) {
var self = this,
entitiesMap = [],
color = 0,
colors = [],
ids = [],
pickingGeometry = new THREE.Geometry(),
pickingMaterial = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } ),
pickingScene = new THREE.Scene(),
pickingTexture = new THREE.WebGLRenderTarget( this._renderer.domElement.width, this._renderer.domElement.height),
cloneMesh,
entities = inGroup ?
engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();
pickingTexture.generateMipmaps = false;
//Go over each entity, change its color into its ID
_.forEach(entities, function(entity) {
if(undefined == entity.threeRenderable) {
return ;
}
//Clone entity
cloneMesh = entity.threeRenderable.mesh().clone();
cloneMesh.material = entity.threeRenderable.mesh().material.clone();
cloneMesh.material.map = null;
cloneMesh.material.vertexColors = THREE.VertexColors;
cloneMesh.geometry = entity.threeRenderable.mesh().geometry.clone();
cloneMesh.position.copy( entity.threeRenderable.mesh().position );
cloneMesh.rotation.copy( entity.threeRenderable.mesh().rotation );
cloneMesh.scale.copy( entity.threeRenderable.mesh().scale );
//Cancel shadow
cloneMesh.castShadow = false;
cloneMesh.receiveShadow = false;
//Set color as entity ID
entitiesMap[color] = entity.id();
self._applyVertexColors(cloneMesh.geometry, new THREE.Color( color ) );
color++;
THREE.GeometryUtils.merge( pickingGeometry, cloneMesh);
});
pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
//render the picking scene off-screen
this._renderer.render(pickingScene, this._objs[this._mainCamera], pickingTexture );
var gl = this._renderer.getContext();
//read the pixel under the mouse from the texture
var pixelBuffer = new Uint8Array( 4 * width * height );
gl.readPixels( x, this._renderer.domElement.height - z, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer );
//Convert RGB in the selected area back to color
for(var i=0; i<pixelBuffer.length; i+=4) {
if( 0 == pixelBuffer[i] && 0 == pixelBuffer[i+1] && 0 == pixelBuffer[i+2] && 0 == pixelBuffer[i+3] ) {
continue;
}
color = ( pixelBuffer[i] << 16 ) | ( pixelBuffer[i+1] << 8 ) | ( pixelBuffer[i+2] );
colors.push(color);
}
colors = _.unique(colors);
//Convert colors to ids
_.forEach(colors, function(color) {
ids.push(entitiesMap[color]);
});
return ids;
}
The line engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();
just return an array of entities, which in turn, I iterate over the entities:
_.forEach(entities, function(entity) { ...
Only entities that have the 'threeRenderable' property (object) are visible, therefore, I ignore those that doesn't have it:
if(undefined == entity.threeRenderable) {
return ;
}
then I merge the entity's cloned mesh with with the pickingGeometry:
THREE.GeometryUtils.merge( pickingGeometry, cloneMesh);
eventually, I add the pickingGeometry to the pickingScene:
pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
Then I read the colors of the selected area, and return an array of IDs.
You can checkout the Node.js game engine I wrote back then.
I've wanted to implement something like this and I choose a very different method - maybe much worse, I don't really know - but much easier to do IMO, so I put it here in case someone wants it.
Basically, I used only 2 raycasts to know the first and last points of the selection rectangle, projected on my ground plane, and iterate over my objects to know which ones are in.
Some very basic code:
function onDocumentMouseDown(event) {
// usual Raycaster stuff ...
// get the ground intersection
var intersects = raycaster.intersectObject(ground);
GlobalGroundSelection = {
screen: { x: event.clientX, y: event.clientY },
ground: intersects[0].point
};
}
function onDocumentMouseUp(event) {
// ends a ground selection
if (GlobalGroundSelection) {
// usual Raycaster stuff ...
// get the ground intersection
var intersects = raycaster.intersectObjects(ground);
var selection = {
begins: GlobalGroundSelection.ground,
ends: intersects[0].point
};
GlobalGroundSelection = null;
selectCharactersInZone(selection.begins, selection.ends);
}
}
function onDocumentMouseMove(event) {
if (GlobalGroundSelection) {
// in a selection, draw a rectangle
var p1 = GlobalGroundSelection.screen,
p2 = { x: event.clientX, y: event.clientY };
/* with these coordinates
left: p1.x > p2.x ? p2.x : p1.x,
top: p1.y > p2.y ? p2.y : p1.y,
width: Math.abs(p1.x - p2.x),
height: Math.abs(p1.y - p2.y)
*/
}
}
Here is my select function:
function selectCharactersInZone (start, end) {
var selected = _.filter( SELECTABLE_OBJECTS , function(object) {
// warning: this ignore the Y elevation value
var itsin = object.position.x > start.x
&& object.position.z > start.z
&& object.position.x < end.x
&& object.position.z < end.z;
return itsin;
});
return selected;
}
Some warnings: as far as I know, this technique is only usable when you don't care about Y positions AND your selection is a basic rectangle.
My 2c

Resources