I have a set of 3D shapes (pyramid, cube, octahedron, prism etc.) and I need to build described sphere around each of them. It is easy to do so using geometry.boundingSphere as it has radius of the described sphere. But if I scale an object the bounding sphere is not being updated. Is it possible to update bounding sphere relatively to the scale?
Using Three.js 0.129.
const { position } = entity.object3D;
const mesh = entity.getObject3D('mesh') as THREE.Mesh;
mesh.geometry.computeBoundingSphere();
const { radius } = mesh.geometry.boundingSphere;
createSphere(radius, position);
The geometry.boundingSphere property represents the geometry. You could technically have two meshes with different scales share the same geometry, so you would want to maintain the geometry's original bounding sphere, and then compute a new one for each mesh, individually.
One problem with scaling the bounding sphere is that you can scale your mesh in x, y, and z separately, and even invert vertex position values given negative scaling values. unequal scale values would lead to it being less of a sphere, and more of a spheroid, which would not help you in math.
What you can do is recompute a bounding sphere for your mesh, given its updated world transformation matrix. I suggest using the world matrix because other ancestors of your mesh could also influence scale in unpredictable ways.
// Given:
// (THREE.Mesh) yourMesh
// Copy the geometry
let geoClone = yourMesh.geometry.clone() // really bad for memory, but it's simple/easy
// Apply the world transformation to the copy (updates vertex positions)
geoClone.applyMatrix4( yourMesh.matrixWorld )
// Convert the vertices into Vector3s (also bad for memeory)
let vertices = []
let pos = geoClone.attributes.position.array
for( let i = 0, l = pos.length; i < l; i += 3 ){
vertices.push( new THREE.Vector3( pos[i], pos[i+1], pos[i+2] ) )
}
// Create and set your mesh's bounding sphere
yourMesh.userData.boundingSphereWorld = new THREE.Sphere()
yourMesh.userData.boundingSphereWorld.setFromPoints( vertices )
This will create a world-aligned bounding sphere for your mesh. If you want one based on local transformations, you can follow the same idea using the local yourMesh.matrix matrix instead. Just know that your sphere's center will then be based on your mesh's local transformation/rotation, not just its scale.
Related
I'm looking to project a texture onto the surface of a mesh in ThreeJS.
https://www.lanyardmarket.com/en/printed-tshirt
This link achieves the result i'm looking for however i'm not sure how they achieved it.
.
I'll update this post as I research however if anyone knows how to project a ThreeJS texture onto a mesh i'd love to know.
Thanks
Working example you may find here: https://jsfiddle.net/mmalex/pcjbysn1/
BufferGeometry stores texture coordinates in 'uv' attribute, you can add it with BufferGeometry.addAttribute and access it through geom.attributes.uv.array.
let uvcoords = [];
let vertexCount = geom.attributes.position.array.length / 3;
// allocate array of UV coordinates (2 floats per each vertex)
uvcoords.length = 2 * vertCount;
if (geom.attributes.uv === undefined) {
geom.addAttribute('uv', new THREE.Float32BufferAttribute(uvcoords, 2));
}
Now all you need is to "project" mesh vertices onto some 3D plane. These projection coordinates will appear your UV coordinates.
In general case, you would need to do Plane.projectPoint for each vertex. This approach is straightforward and can be optimized with pre-rotating the mesh so that vertex x and y components become u and v accordingly. This you will find in my jsfiddle.
I'm currently centering objects/models (mesh) in my scene by calling mesh.geometry.center()
This helps me place the object in the center based on its bounding box.
This works perfectly for static objects that don't ever change size (for example lets say I want to render a cardboard box and I'll never change its proportions).
However, I also have some complex 3D models (for example - a bowling ball, a couple of jagged rocks and the Eiffel Tower).
At the moment, each of these models will be placed centrally based on its bounding box. This means that the jaggedy points of the rocks, the curved base of the bowling ball, or the first section of the base of the Eiffel Tower will jut through / clip through the base of the cardboard box (or, conversely, if not scaled appropriately, will just float in mid-air)
What I need is a way find the center of each mesh on only the X and Y axis to allow me to place these models flat on an XY plane without having to manually adjust the Z value. Is this possible?
A general way to do this for objects or trees of objects...
Here I get the bounds of the object.. then scale it so it ranges to 1 on the largest axis... and centers it on x,z axis, and seated on the y.
var cent = new THREE.Vector3();
var size = new THREE.Vector3();
var bbox = new THREE.Box3().setFromObject(yourObject);
bbox.getCenter(cent);
bbox.getSize(size);
//Rescale the object to normalized space
var maxAxis = Math.max(size.x, size.y, size.z);
yourObject.scale.multiplyScalar(1.0 / maxAxis);
//Now get the updated/scaled bounding box again..
bbox.setFromObject(yourObject);
bbox.getCenter(cent);
bbox.getSize(size);
yourObject.position.x = -cent.x;
yourObject.position.y = 0;
yourObject.position.z = -cent.z;
I'm trying to make a linear regression plane visualization tool for a math project. Currently I have the math parts completed, but I am not sure how to graph the plane. I have a equation in the form of z=C+xD+yE, where C, D, and E are known constants. How do I graph the plane using these information? Thanks.
github page: https://saxocellphone.github.io/LAProject/
z=C+xD+yE
This equation gives full information about the plane. What else you need to graph (plot, draw?) it? Probably it depends on your graphic software possibilities.
Canonical form of given equation:
xD + yE - z + C = 0
Normal to the plane is (D, E, -1). Distance to the coordinate origin Abs(C)/Sqrt(D^2+E^2+1).
Plane intersects coordinate axes at values (-C/D), (-C/E), (C)
I see your problem is not with math, but with three,
as WestLangley pointed out in his comment you can play with rotations etc. or create a simple triangle which is the easiest way
since you have your equation for the plane create 3 points to form a triangle
// z=C+xD+yE
// i assume here that the plane is not aligned with any axis
// and does not pass through the origin, otherwise choose the points in another way
var point1 = new THREE.Vector3(-C/D,0,0);//x axis intersection
var point2 = new THREE.Vector3(0,-C/E,0);//y axis intersection
var point3 = new THREE.Vector3(0,0,C);//z axis intersection
now form a new geometry as in How to make a custom triangle in three.js
var geom = new THREE.Geometry();
geom.vertices.push(point1);// adding vertices to geometry
geom.vertices.push(point2);
geom.vertices.push(point3);
// telling geometry that vertices 0,1,2 form a face = triangle
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
create a simple material and add it to a scene
var material = new THREE.MeshBasicMaterial({
color: 0xff0000, // RGB hex color for material
side: THREE.DoubleSide // do not hide object when viewing from back
});
scene.add(new THREE.Mesh(geometry,material));
that should get you going, you can add another triangles, or make it larger with choosing points that are further apart
I'm working with Three.js, version 68. I'm using the same method for collision detection as this guy is using here, which is great most of the time (A big "thank you" goes out to the author!): http://stemkoski.github.io/Three.js/Collision-Detection.html
Here is a link to the source if you want to download it from github. Just look for Collision-Detection.html: https://github.com/stemkoski/stemkoski.github.com
Here is the code that is important to the collision detection:
var MovingCube;
var collidableMeshList = [];
var wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(100, 50, -100);
scene.add(wall);
collidableMeshList.push(wall);
var wall = new THREE.Mesh(wallGeometry, wireMaterial);
wall.position.set(100, 50, -100);
scene.add(wall);
var wall2 = new THREE.Mesh(wallGeometry, wallMaterial);
wall2.position.set(-150, 50, 0);
wall2.rotation.y = 3.14159 / 2;
scene.add(wall2);
collidableMeshList.push(wall2);
var wall2 = new THREE.Mesh(wallGeometry, wireMaterial);
wall2.position.set(-150, 50, 0);
wall2.rotation.y = 3.14159 / 2;
scene.add(wall2);
var cubeGeometry = new THREE.CubeGeometry(50,50,50,1,1,1);
var wireMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe:true } );
MovingCube = new THREE.Mesh( cubeGeometry, wireMaterial );
MovingCube.position.set(0, 25.1, 0);
// collision detection:
// determines if any of the rays from the cube's origin to each vertex
// intersects any face of a mesh in the array of target meshes
// for increased collision accuracy, add more vertices to the cube;
// for example, new THREE.CubeGeometry( 64, 64, 64, 8, 8, 8, wireMaterial )
// HOWEVER: when the origin of the ray is within the target mesh, collisions do not occur
var originPoint = MovingCube.position.clone();
for (var vertexIndex = 0; vertexIndex < MovingCube.geometry.vertices.length; vertexIndex++)
{
var localVertex = MovingCube.geometry.vertices[vertexIndex].clone();
var globalVertex = localVertex.applyMatrix4( MovingCube.matrix );
var directionVector = globalVertex.sub( MovingCube.position );
var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
appendText(" Hit ");
}
This works great most of the time, but there are times when I can move the cube partially into the wall, and it won't register a collision. For example, look at this image:
It should say "Hit" in the top-left corner where there are just a bunch of dots, and it's not.
NOTE: I also tried his suggestion and did the following, but it didn't seem to help much:
THREE.BoxGeometry( 64, 64, 64, 8, 8, 8, wireMaterial ) // BoxGeometry is used in version 68 instead of CubeGeometry
Does anyone know how this method could be more accurate? Another question: Does anyone know what the following if statement is for, i.e. why does the object's distance have to be less than the length of the direction vector?:
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
To answer your last question first: that line detects whether the collision happened inside your MovingCube. Your raycasting code casts a ray from the MovingCube's position towards each of its vertices. Anything that the ray intersects with is returned, along with the distance from the MovingCube's position at which the intersected object was found (collisionResults[0].distance). That distance is compared with the distance from the MovingCube's position to the relevant vertex. If the distance to the collision is less than the distance to the vertex, the collision happened inside the cube.
Raycasting is a poor method of collision detection because it only detects collisions in the exact directions rays are cast. It also has some additional edge cases. For example, if the ray is cast from inside another object, the other object might not be considered to be colliding. As another example, raycasting in Three.js uses bounding spheres (or, if unavailable, bounding boxes) to calculate ray intersection, so rays can "intersect" with objects even if they wouldn't hit them visually.
If you're only dealing with spheres or upright cuboids, it's straightforward math to check collision. (That's why Three.js uses bounding spheres and bounding boxes - and most applications that need to do collision checking use secondary collision-only geometries that are less complicated than the rendered ones.) Spheres are colliding if the distance between their centers is less than the sum of their radii. Boxes are colliding if the edges overlap (e.g. if the left edge of box 1 is to the left of the right edge of box 2, and the boxes are within a vertical distance the sum of their half-heights and a horizontal distance the sum of their half-lengths).
For certain applications you can also use voxels, e.g. divide the world into cubical units, do box math, and say that two objects are colliding if they overlap with the same cube-unit.
For more complex applications, you'll probably want to use a library like Ammo.js, Cannon.js, or Physi.js.
The reason raycasting is appealing is because it's workable with more complex geometries without using a library. As you've discovered, however, it's less than perfect. :-)
I wrote a book called Game Development with Three.js which goes into this topic in some depth. (I won't link to it here because I'm not here to promote it, but you can Google it if you're interested.) The book comes with sample code that shows how to do basic collision detection, including full code for a 3D capture-the-flag game.
[EDIT: see this jsfiddle for a live example plus accompanying code]
Using three.js I'm trying to render out some celestial bodies with prominent features.
Unfortunately no examples are provided on how to apply spherical heightmaps with threejs but they do have an example where a heightmap is applied to a plane.
I took said example and modified it to use a SphereGeometry(); instead of a PlaneGeometry();
Obviously the geometry of a sphere is critically different from that of a plane, and when rendering out the results the sphere shows as a flat piece of texture.
The heightmap code for planes:
var plane = new THREE.PlaneGeometry( 2000, 2000, quality - 1, quality - 1 );
plane.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
for ( var i = 0, l = plane.vertices.length; i < l; i ++ ) {
var x = i % quality, y = ~~ ( i / quality );
plane.vertices[ i ].y = data[ ( x * step ) + ( y * step ) * 1024 ] * 2 - 128;
}
Now I'm guessing the solution is relatively simple: instead of mapping to the plane's 2d coordinate in the for loop, it has to find the surface coordinate of the sphere in 3d space. Unfortunately I'm not really a pro at 3d maths so I'm pretty much stuck at this point.
An example of the heightmap applied to the sphere and all code is put together in this jsfiddle. An updated jsfiddle shows an altered sphere but with random data instead of the height map data.
I know for a fact you can distort the sphere's 3d points to generate these surface details, but I'd like to do so using a heightmap. This JSFiddle is as far as I got- it'll randomly alter points to give a rocky appearance to the sphere, but obviously doesnt look very natural.
EDIT: The following is the logic required I wish to implement that maps heightmap data to a sphere.
In order to map the data to a sphere, we will need to map coordinates from a simple spherical coordinate system (longitude φ, latitude θ, radius r) to Cartesian coordinates (x, y, z). Just as in normal height-mapping the data value at (x, y) is mapped to z, we will map the value at (φ, θ) to r. This transformation comes down to:
x = r × cos φ × sin θ
y = r × sin φ × sin θ
z = r × cos θ
r = Rdefault + Rscale × d(φ, θ)
The parameters Rdefault and Rscale can be used to control the size of the sphere and the height map on it.
Uses vector3 to move each vertices:
var vector = new THREE.Vector3()
vector.set(geometry.vertices[i].x, geometry.vertices[i].y, geometry.vertices[i].z);
vector.setLength(h);
geometry.vertices[i].x = vector.x;
geometry.vertices[i].y = vector.y;
geometry.vertices[i].z = vector.z;
Example: http://jsfiddle.net/damienlabat/b3or4up3/
If you want to apply a 2D map onto the 3D sphere surface, you will need to use UVs of the sphere. Fortunately, UVs come with THREE.SphereGeometry by default.
The UVs are stored per-face though, so you will need to iterate through the faces array.
For each face in the geometry:
Read the corresponding UV value in the FaceVertexUvs array for each associated vertex.
Read the height map value using that UV location.
Shift the vertex along the vertex normal by that value. The faces array gives the vertex index, which you can use to index into the vertices array to get/set the vertex position.
After this is all done, set verticesNeedUpdate to true to update the vertices.