I am working on a project of mine that has a plane with a grid that allows you to just snap on objects. Where I am stuck is, I can't seem to figure out how to tell if an object overhangs the plane, than don't add it if the user triggers a click.
Currently:
event.preventDefault();
this.mouse.x = ( event.clientX / this.renderer.domElement.width ) * 2 - 1;
this.mouse.y = - ( event.clientY / this.renderer.domElement.height ) * 2 + 1;
this.raycaster.setFromCamera( this.mouse, this.camera.cam );
var intersects = this.raycaster.intersectObjects( this.blocks );
if ( intersects.length > 0 ) {
var intersect = intersects[ 0 ];
if ( this.isShiftDown ) { // see if shift is down to remove
if ( intersect.object != this.plane ) {
this.scene.remove( intersect.object );
this.blocks.splice( this.blocks.indexOf( intersect.object ), 1 );
}
} else if(intersect.object == this.plane ){ // add room to canvas
var voxel = new THREE.Mesh( this.room.cubeGeometry.clone(), this.room.cubeMaterial.clone() );
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
this.scene.add( voxel );
this.blocks.push( voxel );
var domEvents = new THREEx.DomEvents(this.camera.cam, this.renderer.domElement)
console.log(voxel);
// DOM events for inside 3d rendering
domEvents.addEventListener(voxel, 'mouseover', this.onDocumentMouseOverCube.bind(this),false);
domEvents.addEventListener(voxel, 'mouseout', this.onDocumentMouseOutCube.bind(this),false);
}
this.render();
}
So what happens here is: first we get our mouse position - send it to our raycaster to setup. Than we check to see if we have intersected any objects. Now this is all great except. If an object is bigger than a single 20x20 slot, it will over hang the plane canvas. I want to be able to deny the user from placing anything if the object over hangs our plane. The plane object is always at position this.blocks[0] So as you can see in the else if we check to see if its on plane. But I am not accounting for, excess of object overhang
Any suggestions on the best way to accomplish this?
Compute the bounding box of your object.
var voxel = new THREE.Mesh( this.room.cubeGeometry.clone(), this.room.cubeMaterial.clone() );
voxel.geometry.computeBoundingBox();
When the user clicks on the plane, move the object there and compare the intersection coordinate (x,z) with the bounding box min and max (x,z) values.
If one of the bounding box values is bigger or smaller than the plane, then it overhangs.
This is just an idea, I have not tested this. If you need help with the code, let me know.
As per Falk's request to share my function I created off of his answer.
When dealing with our volex on creation the key here is to set a cube.geometry.computeBoundingBox() - on every volex creation. Reason be, by default we wont have a min or max bounding size. Which to be able to grab our depth and width of our object this is crucial.
We already know that the user is on a PlaneBufferGeometry or plane in my case. So we pass our volex and plane to a custom function called checkOverlapPlane(volex,plane)
Now in a jiffy, this function grabs our plane and volex max and min boundaries. In addition we grab the volex position which is crucial to figure out the volex position on the x and z axis. We than convert all of our variables to positive numbers only for the fact that it makes it easy to deal with positive numbers and add our different x and z. After converting our intagers to positive intagers we than get our total position of the min and max volex z and the volex x. Which in turn is compared with our plane min x z and max x z to return whether the volex is hanging off of our plane.
Here is the code:
checkOverlapPlane: function(voxel,plane) { // THIS IS USED TO TELL US IF WE ARE OUTSIDE THE GRIDS BOUNDRIES
// Now before doing anything lets check out min and max boundries to see if our x and z are either bigger or smaller
var planeMX = plane.geometry.boundingBox.max.x;
var planeMZ = plane.geometry.boundingBox.max.z;
var planemX = plane.geometry.boundingBox.min.x;
var planemZ = plane.geometry.boundingBox.min.z;
//lets get our voxel min and max vars
var voxelMX = voxel.geometry.boundingBox.max.x;
var voxelMZ = voxel.geometry.boundingBox.max.z;
var voxelmX = voxel.geometry.boundingBox.min.x;
var voxelmZ = voxel.geometry.boundingBox.min.z;
// we need to get our voxel position ad to do some math to see if voxel min and max do not go beyound our boundries for our plane
var voxelPX = voxel.position.x;
var voxelPZ = voxel.position.z;
// lets do some calucation and condition
// first lets turn everything over to positive for some easy calcuation
voxelPX = Math.abs(voxelPX); // positions on world view
voxelPZ = Math.abs(voxelPZ);
voxelmZ = Math.abs(voxelmZ); // min and max values of our voxel
voxelmX = Math.abs(voxelmX);
voxelMZ = Math.abs(voxelMZ);
voxelMX = Math.abs(voxelMX);
planemZ = Math.abs(planemZ); // min and max values of our plane our voxels are on
planemX = Math.abs(planemX);
planeMZ = Math.abs(planeMZ);
planeMX = Math.abs(planeMX);
// now lets calucate X and Z to see if total goes over any of our planes min and max X or Y - remember everything is position but so we can add it to see if its above - we will not actually change the direct objects values
var totalPositionVoxelminZ = (voxelPZ + voxelmZ);
var totalPositionVoxelminX = (voxelPX + voxelmX);
var totalPositionVoxelMAXZ = (voxelPZ + voxelMZ);
var totalPositionVoxelMAXX = (voxelPX + voxelMX);
// now lets compare
if(totalPositionVoxelminZ > planemZ)
return false; // do nothing
else if(totalPositionVoxelminX > planemX)
return false; // do nothing
else if(totalPositionVoxelMAXZ > planeMZ)
return false; // do nothing
else if(totalPositionVoxelMAXX > planeMX)
return false; // do nothing
else
return true;
},
Here is the results:
Feel free to use and incorporate this in any project!
Related
I have a plane with a detail of 400 by 400.
When defining the y positions of all of the vertices, I do this.
var position = floorGeometry.attributes.position;
for ( var i = 0; i <= complexity + 1; i ++ ) {
for (var i2 = 0; i2 <= complexity; i2 ++) {
vertex.fromBufferAttribute( position, i * complexity + i2);
var x = vertex.x;
var y = vertex.z;
vertex.y = noise(x,y)
position.setXYZ(i * complexity + i2, vertex.x, vertex.y, vertex.z );
}
}
Complexity represents the detail of the plane.
As you can see... I use geometry.attributes.position to access the vertices, but it is important to note that this stores all of the "sqaure" coordinates
But when it comes to the color attribute... it actually uses the points (and expects an array) of each and every vertex of the tris that make up the plane in a specific order...
What I am doing is making an array of colors (3 elements per vertex representing rgb) and then trying to add it as an attribute to the geometry, and I am trying to make vertices of different heights different colors. For example
count = floorGeometry.attributes.position.count;
var colors = [];
for ( var i = 0; i < count; i ++ ) {
vertex.fromBufferAttribute( position, Math.floor(i)); //<---- NOTE
if (vertex.y > 500) {
colors.push(1,0,0);
}
else colors.push(0,1,0);
}
At the point in the code with the comment "NOTE" I dont know what i am doing here in terms of turing an index from that square system to the color attributes tri based vertex system.
Any ideas? Should I try to access the vertices of the tri based system instead? Is there a mathematical way to do this correctly?
The simple solution is to not use:
vertex.fromBufferAttribute( position, index );
because that uses the square system I discussed in my question, instead use:
geometry.attributes.position.getY(i);
or .getX(i) or .getZ(i) because these use the vertices of the tris!
I'm working on an AE project where around 50 Emojis should have a drop shadow on the floor.To make things easier I tried to add an expression that auto shrinks and grows the shadows based on the distance of the emoji to the floor.
Here is what I've tried
Drop Shadow Approach
You can see that the shadow grows and shrinks but in the wrong direction. So when emoji comes closer to the floor it shrinks and when the distance is more it grows. I need the opposite of the current behavior.
How do I achieve that?
This is the expression I've used for the scale property of the shadow layer. Shadow layer is separate from the emoji layer. So I have a composition with only 2 layers.
var y = thisComp.layer("smile").position[1];
var dist = Math.sqrt( Math.pow((this.position[0]-this.position[0]), 2) + Math.pow((this.position[1]-y), 2) );
newValue = dist ;
xScale = newValue;
yScale = newValue;
[xScale,yScale]
Thanks for your time.
The basic concept here is mapping values from one range to another. You want to say that (for example) as the distance changes between 0 and 100, the scale should change proportionally between 1 and 0.
function map ( x, oldMin, oldMax, newMin, newMax ) {
return newMin + ( x - oldMin ) / ( oldMax - oldMin ) * ( newMax - newMin );
}
var minDistance = 0;
var maxDistance = 100;
var maxScale = 1;
var minScale = 0;
xScale = yScale = map( dist, minDistance, maxDistance, maxScale, minScale );
I am using the following code to scale and center a msgpack compressed object loaded using the ObjectLoader and it is not working. I think that my object has a rotation on it, and hence causing weird behaviors. On some objects, it successfully centers, but on others the centering is offset and scaling isn't right either.
In this snippet, result is the scene from the ObjectLoader. My thought was that the object was not very well formed, but I'm not sure. I wanted the table on the image or any other user entered mesh to be on the top of the grid, centered and scaled so that the maximum size is 1 unit.
Each square measures 0.25, the axis are at 0,0,0 http://i.stack.imgur.com/fkKYC.png
// result is a threejs scene
var geometry = result.children[0].geometry;
var mesh = result.children[0];
geometry.computeBoundingBox();
var middle = new THREE.Vector3();
middle.x = ( geometry.boundingBox.max.x + geometry.boundingBox.min.x ) / 2;
middle.y = -geometry.boundingBox.min.y;
middle.z = ( geometry.boundingBox.max.z + geometry.boundingBox.min.z ) / 2;
middle.negate();
mesh.position.copy(middle);
// scales the mesh up to maxsize
var maxSize = 1;
// gets the biggest axis
var maxVal = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
if (maxVal < geometry.boundingBox.max.y - geometry.boundingBox.min.y) {
maxVal = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
}
if (maxVal < geometry.boundingBox.max.z - geometry.boundingBox.min.z) {
maxVal = geometry.boundingBox.max.z - geometry.boundingBox.min.z;
// scales the current size proportional to the maxval, times maxsize
mesh.scale.divideScalar(maxVal * maxSize);
self.scene.add(result);
Instead of calling geometry.computeBoundingBox(); call geometry.center(); then you don't need the middle.x or middle.z and you can just call mesh.translateY() rather than fiddling with middle at all
I have written a small 2D game in javascript that uses a grid where the player starts at position [0,0] and can move an almost infinite distance in either direction.
Now I want to implement A* pathfinding, but I'm having some problems finding the best way to store the world with all it's different obstacles, enemies and terrain. This is what I have tried or thought about so far.
Array of arrays
Here I store the world in an array of arrays [x][y].
var world = [[]];
world[312][11] = 0;
world[312][12] = 0;
world[312][13] = 1;
world[312][14] = 1;
...
This works great with A* pathfinding! It's easy and very fast to access a specific coordinate and populate the world. In the example above I just store passable (0) or impassable (1) terrain, but I can store pretty much whatever I want there. However, this doesn't work very well with negative coordinates like if my players is at [-12][-230]. Negative keys in a javascript array isn't actually part of the array, they won't be included in world.length or world[3].length and from what I understand, it's overall bad practice and might have some impact on the performance as well. I read somewhere that if you are using negative keys in your array, you are doing it wrong.
I would still not pass the entire world into the A* function for obvious reasons. Just a small part close to my player, but the coordinates would correspond to the positions in the array which is easy to work with.
A separate array of arrays just for A* pathfinding
This is where I'm at right now. I have a separate 50x50 grid called pathMap = [[]], that is only used for pathfinding.
var pathMap = [[]];
pathMap[0][0] = 0;
pathMap[0][1] = 0;
pathMap[0][2] = 1;
pathMap[0][3] = 1;
...
It starts at pathMap[0][0] and goes to pathMap[50][50] and is working as an overlay on my current position where I (as the player) will always be in the center position. My real coordinates may be something like [-5195,323], but it translates to pathMap[25][25] and everything close to me is put on the pathMap in relation to my position.
Now this works, but it's a huge mess. All the translations from one coordinate to another back and forth makes my brain hurt. Also, when I get the path back from A*, I have to translate each step of it back to the actual position my element should move to in the real world. I also have to populate the same object into 2 different grids every update which hurts performance a bit as well.
Array of objects
I think this is where I want to be, but I have some issues with this as well.
var world = [];
world[0] = { x: -10, y: 3, impassable: 0 };
world[1] = { x: -10, y: 4, impassable: 0 };
world[2] = { x: -10, y: 5, impassable: 1 };
world[3] = { x: -10, y: 6, impassable: 1 };
...
Works great with negative x or y values! However, it's not as easy to find for instance [10,3] in this array. I have to loop through the entire array to look for an object where x == 10 and y == 3 instead of the very easy and fast approach world[10][3] in the first example. Also, I can't really rely on the coordinates being in the right order using this version, sorting becomes harder, as does other things that was a lot easier with the array of arrays.
Rebuild the game to always be on the positive side
I would prefer not to do this, but I have considered placing the players starting position at something like [1000000,1000000] instead, and making negative coordinates off limits. It seems like a failure if I have to remove the vision I have of endlessness just to make the pathfinding work with less code. I know there will always be some upper or lower limits anyways, but I just want to start at [0,0] and not some arbitrary coordinate for array related reasons.
Other?
In javascript, is there another option that works better and is not described above? I'm very open to suggestions!
Is there a best practice for similar cases?
You have three coordinates system you must distinguish :
the world coordinates.
the world model / path-finding (array) coordinates.
the screen coordinates.
The screen coordinates system depends upon :
the viewport = the canvas. (width, height in pixels).
a camera = (x,y) center in world coordinates + a viewWidth (in world coordinates).
To avoid headaches, build a small abstraction layer that will do the math for you.
You might want to use Object.defineProperty to define properties, that will provide a fluent interface.
var canvas = ... ;
var canvasWidth = canvas.width;
var canvasHeigth = canvas.heigth;
var world = {
width : 1000, // meters
height : 1000, // meters
tileSize : 0.5, // height and width of a tile, in meter
model : null, // 2D array sized ( width/tileSize, XtileSize )
};
// possibles world coordinates range from -width/2 to width/2 ; - height/2 height/2.
var camera = {
x : -1,
y : -1,
viewWidth : 10, // we see 10 meters wide scene
viewHeight : -1 // height is deduced from canvas aspect ratio
};
camera.viewHeight = camera.viewWidth * canvasWidth / canvasHeight ;
Then your character looks like :
// (x,y) is the center of the character in centered world coordinates
// here (0,0) means (500,500) world coords
// (1000,1000) array coords
// (320, 240) screen coords in 640X480
function /*Class*/ Character(x, y) {
var _x=x;
var _y=y;
var _col=0;
var _row=0;
var _sx=0.0;
var _sy=0.0;
var dirty = true;
Object.defineProperty(this,'x',
{ get : function() {return _x; }
set : function(v) { _x=v;
dirty=true; } });
Object.defineProperty(this,'x',
{ get : function() {return _y; }
set : function(v) { _y=v;
dirty=true; } });
Object.defineProperty(this,'col',
{ get : function() {
if (dirty) updateCoords();
return _col; } });
Object.defineProperty(this,'row',
{ get : function() {
if (dirty) updateCoords();
return _row; } });
Object.defineProperty(this,'sx',
{ get : function() {
if (dirty) updateCoords();
return _sx; } });
Object.defineProperty(this,'sy',
{ get : function() {
if (dirty) updateCoords();
return _sy; } });
function updateCoords() {
_row = ( ( _x + 0.5 * world.width )/ world.tileSize ) | 0 ;
_col = ( ( _x + 0.5 * world.height )/ world.tileSize ) | 0 ;
_sx = canvasWidth * ( 0.5 + ( _x - camera.x ) / camera.viewWidth ) ;
_sy = canvasHeight * ( 0.5 + ( _y - camera.y ) / camera.viewHeight ) ;
dirty = false;
}
}
I'm making a game in html5 canvas. I'm using jquery so I can get the click event and the clicks x,y coordinates. I have an array of unit objects and a tiled terrain (also an array). The unit objects have bounding box information, their position, and their type.
What is the most effecient way to map this click event to one of the units?
Loop through the unit objects and determine which was clicked like so:
// 'e' is the DOM event object
// 'c' is the canvas element
// 'units' is the array of unit objects
// (assuming each unit has x/y/width/height props)
var y = e.pageY,
x = e.pageX,
cOffset = $(c).offset(),
clickedUnit;
// Adjust x/y values so we get click position relative to canvas element
x = x - cOffset.top;
y = y - cOffset.left;
// Note, if the canvas element has borders/outlines/margins then you
// will need to take these into account too.
for (var i = -1, l = units.length, unit; ++i < l;) {
unit = units[i];
if (
y > unit.y && y < unit.y + unit.height &&
x > unit.x && x < unit.x + unit.width
) {
// Escape upon finding first matching unit
clickedUnit = unit;
break;
}
}
// Do something with `clickedUnit`
Note, this won't handle complex intersecting objects or z-index issues etc... just a starting point really.