I want to calculate the camera near and far distance based on an axis aligned bounding box using three.js.
I tried this way:
getNearFar = function(camera, box) {
var view_matrix = new THREE.Matrix4();
view_matrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
var minz = +Infinity; var maxz = -Infinity;
for (var i = 0; i < 8; i++) {
var x = i & 1 ? box.min.x : box.max.x;
var y = i & 2 ? box.min.y : box.max.y;
var z = i & 4 ? box.min.z : box.max.z;
var p = new THREE.Vector3(x, y, z);
p.applyMatrix3(view_matrix);
z = -p.z;
if (z > maxz) maxz = z;
if (z < minz) minz = z;
}
return {'near':minz, 'far':maxz};
};
What I did is to get the camera view matrix, and convert the 8 corner points of the box into camera space. The near and far distance should be the min and max z. But it does not give the correct answer. Where did I go wrong?
I found out what's wrong:
it should be p.applyMatrix4()
Related
I try to seperate a list of point into all basic angle
Here's my code
var increment =15/50;
var distance = 20;
var count = 0;
for( theta=0;theta <50; theta++) {
var newX = centerX +distance*Math.cos(theta*2*Math.PI*increment);
var newY = centerY +distance*Math.sin(theta*2*Math.PI*increment);
cxt.fillText("o",newX, newY);
count++;
if(count%5===0){
distance=distance+20;
}
}
Output:
I try to modify the increment many times but either they didn't make into straght lines or all the points turn into a mess
What is the exact increment number or function for this?
Problem 1
No need for count - it is being incremented in exactly the same fashion as theta:
var increment =15/50;
var distance = 20;
for( theta=0;theta <50; theta++) {
var newX = centerX +distance*Math.cos(theta*2*Math.PI*increment);
var newY = centerY +distance*Math.sin(theta*2*Math.PI*increment);
cxt.fillText("o",newX, newY);
if(theta%5===0){ // just use theta here
distance=distance+20;
}
}
Problem 2
The if statement increments distance once every 5 points, which means each "ring" only contains 5 points, whereas they should have as many points as basic angles. The logic should therefore be:
if (theta % 16 === 0) { // there are 16 "basic angles"
distance += 20;
}
Problem 3
1 is not an integer multiple of the fractional increment 15/50, so the second "ring" of points will not line up with the first - there will be an angular offset of 2*PI/5 between consecutive rings.
It is more efficient to separate the single loop into two nested loops, as suggested by Damien. The outer loop should increment the angle as the trigonometric functions are expensive.
var num_basic_angles = 16; // number of basic angles
var separation = 20; // distance between each point
var num_points; // number of points per angle
for (var angle = 0; angle < num_basic_angles; angle++) {
var theta = 2*Math.PI*angle/num_basic_angles;
var cos_t = Math.cos(theta);
var sin_t = Math.sin(theta);
for (var point = 0; point < num_points; point++) {
var x = centerX + distance * point * cos_t;
var y = centerY + distance * point * sin_t;
cxt.fillText("o",x,y);
}
}
If you really want to do things in a single loop, use theta = Math.floor(i / num_basic_angles), point = i % num_basic_angles, where i is the loop counter from 0 to num_basic_angles * num_points - 1.
Problem 4
The "basic angles" are not evenly distributed, so incrementing by a fixed value will not work.
No need to be too smart here, because these basic angles are arbitrarily defined anyway. Just store them in an array:
var basic_angles = [0,30,45,60,90,120,135,150,180,210,224,240,270,300,315,330];
var separation = 20; // distance between each point
var num_points; // number of points per angle
for (var angle = 0; angle < basic_angles.length; angle++) {
var theta = Math.PI/180.0*basic_angles[angle];
var cos_t = Math.cos(theta);
var sin_t = Math.sin(theta);
for (var point = 0; point < num_points; point++) {
var x = centerX + distance * point * cos_t;
var y = centerY + distance * point * sin_t;
cxt.fillText("o",x,y);
}
}
I know Rectangle is axis aligned, that's fine, I just can't figure out how to create a rectangle so it is always encompassing the entire sprite, regardless of rotation. I have been looking everywhere for an answer but I can't get a straight one anywhere.
For example:
Assuming the origin point is the middle of the texture, how can I go about this?
EDIT
Fiddling around with it a little, I've gotten this far:
public Rectangle BoundingBox
{
get
{
var cos = Math.Cos(SpriteAngle);
var sin = Math.Cos(SpriteAngle);
var t1_opp = Width * cos;
var t1_adj = Math.Sqrt(Math.Pow(Width, 2) - Math.Pow(t1_opp, 2));
var t2_opp = Height * sin;
var t2_adj = Math.Sqrt(Math.Pow(Height, 2) - Math.Pow(t2_opp, 2));
int w = Math.Abs((int)(t1_opp + t2_opp));
int h = Math.Abs((int)(t1_adj + t2_adj));
int x = Math.Abs((int)(Position.X) - (w / 2));
int y = Math.Abs((int)(Position.Y) - (h / 2));
return new Rectangle(x, y, w, h);
}
}
(doing this off the top of my head.. but the principle should work)
Create a matrix to rotate around the center of the rectangle - that is a translate of -(x+width/2), -(y+height/2)
followed by a rotation of angle
followed by a translate of (x+width/2), (y+height/2)
Use Vector2.Transform to transform each corner of the original rectangle
Then make a new rectangle with
x = min(p1.x, p2.x, p3.x, p4.x)
width = max(p1.x, p2.x, p3.x, p4.x) - x
similar for y
Sorry this is coming so late, but I figured this out a while ago and forgot to post an answer.
public virtual Rectangle BoundingBox
{
get
{
int x, y, w, h;
if (Angle != 0)
{
var cos = Math.Abs(Math.Cos(Angle));
var sin = Math.Abs(Math.Sin(Angle));
var t1_opp = Width * cos;
var t1_adj = Math.Sqrt(Math.Pow(Width, 2) - Math.Pow(t1_opp, 2));
var t2_opp = Height * sin;
var t2_adj = Math.Sqrt(Math.Pow(Height, 2) - Math.Pow(t2_opp, 2));
w = (int)(t1_opp + t2_opp);
h = (int)(t1_adj + t2_adj);
x = (int)(Position.X - (w / 2));
y = (int)(Position.Y - (h / 2));
}
else
{
x = (int)Position.X;
y = (int)Position.Y;
w = Width;
h = Height;
}
return new Rectangle(x, y, w, h);
}
}
This is it here. In my work in the edit, I accidentally had Math.Cos in the sin variable, which didn't help.
So it's just basic trigonometry. If the textures angle is something other than zero, calculate the sides of the two triangles formed by the width and the height, and use the sides as the values for the width and the height, then center the rectangle around the texture. If that makes sense.
Here's a picture to help explain:
Here's a gif of the final result:
I'm trying to render a matrix of points in Three.js but I need to treat each particle in the cloud as an individual "pixel" for which I can change the color of each on the fly. I figured out how to basically render the point cloud, and can set the initial color, but cannot figure out how to change the color of each point once it's set.
I'm generating the point cloud like this:
function generateRegularPointcloud( color, width, length ) {
var geometry = new THREE.Geometry();
var numPoints = width * length;
var colors = [];
var k = 0;
for( var i = 0; i < width; i++ ) {
for( var j = 0; j < length; j++ ) {
var u = i / width;
var v = j / length;
var x = u - 0.5;
var y = 0;
var z = v - 0.5;
var v = new THREE.Vector3( x,y,z );
var intensity = ( y + 0.1 ) * 7;
colors[ 3 * k ] = color.r * intensity;
colors[ 3 * k + 1 ] = color.g * intensity;
colors[ 3 * k + 2 ] = color.b * intensity;
geometry.vertices.push( v );
colors[ k ] = ( color.clone().multiplyScalar( intensity ) );
k++;
}
}
geometry.colors = colors;
geometry.computeBoundingBox();
var material = new THREE.PointCloudMaterial( { size: pointSize, vertexColors: THREE.VertexColors } );
var pointcloud = new THREE.PointCloud( geometry, material );
return pointcloud;
}
My basic code is here: http://jsfiddle.net/dg34sbsk/
Any idea how to change each point color separately and dynamically? (Data for the colors will be coming in from a web service).
You can directly change its's value pointclouds[0].geometry.colors=... and after that pointclouds[0].geometry.colorsNeedUpdate=true.
To set each point's color just set the colors's children's value like pointclouds[0].geometry.colors[22]=new THREE.Color("rgb(255,0,0)");.
see this:http://jsfiddle.net/aboutqx/dg34sbsk/2/ .click and you will see the color of one point changes.
I'm making a top-down shooter and the player's gun is offset from the coordinates of the object. I'm using GameMaker:Studio, so the x and y coords are the center of the object. The offset of the image is set here:
bullet_offset_x = 30;
bullet_offset_y = 28;
And here is the code for shooting the gun:
var xpos = x + (bullet_offset_x * cos(degtorad(direction))) - (bullet_offset_y * sin(degtorad(direction)));
var ypos = y + (bullet_offset_x * sin(degtorad(direction))) + (bullet_offset_y * cos(degtorad(direction)));
var flash = instance_create(xpos, ypos, obj_flash);
with (flash){
direction = other.direction;
image_angle = other.direction;
}
I'm using the following formula for placing the muzzle flash:
x' = xcos(angle) - ysin(angle)
y' = xsin(angle) + ycos(angle)
Therefore:
xpos = x + x' and ypos = x + y'
However, when I run the code, the muzzle flash is correctly positioned when the angle is 0/360, but is off otherwise. Am I calculating this wrong?
IMAGES:
Correct
Incorrect
You need to use lengthdir_x and lengthdir_y functions, like:
var xpos = x + lengthdir_x(offset_distance, offset_angle + image_angle); // or direction
var ypos = y + lengthdir_y(offset_distance, offset_angle + image_angle);
var flash = instance_create(xpos, ypos, obj_flash);
flash.direction = direction;
flash.image_angle = direction;
little example here
To calculate the values to be substituted into the formula, you can use this program.
Originally it was made in Russian, but I have translated it into English. My English is terrible, but I hope you will be able to understand it.
upd: Example with offsets:
var delta_x = 60;
var delta_y = -70;
var angle = point_direction(0, 0, delta_x, delta_y);
var distance = point_distance(0, 0, delta_x, delta_y);
var xpos = x + lengthdir_x(distance, image_angle + angle);
var ypos = y + lengthdir_y(distance, image_angle + angle);
var obj = instance_create(xpos, ypos, obj_flash);
obj.image_angle = image_angle;
When your sprite has an angle of 0, your muzzle flash still at an angle of invtan(28/30) in relation to the sprite. Therefore, the angle that the flash must be placed at in relation to the rotation of the sprite can be given by
flashRotation = spriteRotationDegrees - invtan(28/30) \\you can change this to radians
Once that is found, the positions can be found by:
var x_pos = sprite_x_pos + Math.Sqrt(28^2 + 30^2)cos(flashRotation);
var y_pos = sprite_y_pos + Math.Sqrt(28^2 + 30^2)sin(flashRotation);
The actual angle of rotation of the flash (which way it points) will be the same angle as the sprite.
You may need to play with the flashRotaion equation depending upon which way is counted as a positive rotation.
So I have a heightmap system which works well enough, however since the THREE.js has updated to r60 which removed the Face4 object, I am having issues.
My code is something like this:
this.buildGeometry = function(){
var geo, len, i, f, y;
geo = new THREE.PlaneGeometry(3000, 3000, 128, 128);
geo.dynamic = true;
geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
this.getHeightData('heightmap.png', function (data) {
len = geo.faces.length;
for(i=0;i<len;i++){
f = geo.faces[i];
if( f ){
y = (data[i].r + data[i].g + data[i].b) / 2;
geo.vertices[f.a].y = y;
geo.vertices[f.b].y = y;
geo.vertices[f.c].y = y;
geo.vertices[f.d].y = y;
}
}
geo.computeFaceNormals();
geo.computeCentroids();
mesh = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({color:0xff0000}) );
scene.add(mesh);
});
};
This works well since a pixel represents each face. How is this done now that the faces are all triangulated?
Similarly I use image maps for model positioning as well. Each pixel matches to the respective Face4 and a desired mesh is placed at its centroid. How can this be accomplished now?
I really miss being able to update the library and do not want to be stuck in r59 anymore =[
This approach works fine on the recent versions (tested on r66).
Notice that the genFn returns the height y given current col and row, maxCol and maxRow (for testing purposes, you can of course replace it with a proper array lookup or from a grayscale image... 64x64 determines the mesh resolution and 1x1 the real world dimensions.
var genFn = function(x, y, X, Y) {
var dx = x/X;
var dy = y/Y;
return (Math.sin(dx*15) + Math.cos(dy * 5) ) * 0.05 + 0.025;
};
var geo = new THREE.PlaneGeometry(1, 1, 64, 64);
geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
var iz, ix,
gridZ1 = geo.widthSegments +1,
gridX1 = geo.heightSegments+1;
for (iz = 0; iz < gridZ1; ++iz) {
for (ix = 0; ix < gridX1; ++ix) {
geo.vertices[ ix + gridX1*iz ].y = genFn(ix, iz, gridX1, gridZ1);
}
}
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeCentroids();
var mesh = new THREE.Mesh(
geo,
mtl
);
scene.add(mesh);