First off: I had another question Creating a 2D polygon in XNA but I answered it myself after a day of frustrating research and testing. There you can find the code I have right now. So here is my next question, as I can't find anything about it. How do I animate a VertexPositionColor[].
To animate a VertexPositionColor[], all you really need to do is modify the elements of the array appropriately and then use the new values in your DrawUserPrimitives() call.
For example, in the Update method of your game:
for ( int index = 0; index < vertices.Length; ++index )
{
vertices[ index ].Color *= (float)System.Math.Sin( gameTime.ElapsedGameTime.Seconds / 60 );
}
Related
I am working with a three js indexed buffered geometry that is in need of some performance improvements.
The visualization I'm working on is essentially a 3D waterfall visualization where each time I receive a new "row" of data I will copy each vertex color and Z position to the row above it, like so:
for(let i = this.geometry.attributes.position.count - 1; i >= this.frameWidth; i--){
let former = i - this.frameWidth;
this.geometry.attributes.position.setZ(i, this.geometry.attributes.position.getZ(former) );
this.geometry.attributes.color.setXYZ(i, this.geometry.attributes.color.getX(former), this.geometry.attributes.color.getY(former), this.geometry.attributes.color.getZ(former));
}
Followed by entering the new Row of data where the frame variable corresponds to the new row to be entered:
for (let i = 0; i < this.frameWidth; i += 1) {
const color = this.colorMap.current.getColor(frame[i]);
this.geometry.attributes.color.setXYZ(i, color.r, color.g, color.b);
this.geometry.attributes.position.setZ(i, frame[i]);
}
this.geometry.attributes.color.needsUpdate = true;
this.geometry.attributes.position.needsUpdate = true;
Being someone whom is more familiar with the works of GPU programming it really seems to me that these for loops can be taken out or done in parallel in some way to vastly improve performance. Is there something I'm missing with threeJS that does these sorts of operations that I'm not aware of?
A second idea that I have is to actually translate the position of the mesh in the Y axes (eliminating the need to copy color and position entries) and re-purposing the vertices of the last row to be the "new" front row where the new data is placed. However it seems that this requires some sort of modification of indices that I'm not exactly sure how it works.
Any ideas and suggestions are extremely helpful :) Thanks!
after loading several gltf files, I am renaming these files and try to reposition the camera so that it is centered and looking at the centroid of the new objects and the whole scene fits within the camera.
But the centering does not always work, sometimes the centroid is calculated somewhere completely different. The following code is ran in render() only once after all objects have been loaded:
var all_centers = [];
scene.updateMatrixWorld();
scene.traverse(function(child){
if (child instanceof THREE.Mesh){
if (child.name.indexOf("_") !== -1){ // the newly imported objects
child.geometry.computeBoundingSphere();
var the_center = new THREE.Vector3();
child.getWorldPosition(the_center);
all_centers.push(the_center);
}
}
});
var the_centroid = getPointsCentroid(all_centers);
var cameraPosition = new THREE.Vector3(the_centroid.x,the_centroid.y,-55);
camera.position.copy(cameraPosition);
camera.lookAt(the_centroid);
and here is the function for the centroid:
function getPointsCentroid(points){
var centroid = [0.,0.,0.];
for(var i = 0; i < points.length; i++) {
var point = points[i];
centroid[0] += point.x;
centroid[1] += point.y;
centroid[2] += point.z;
}
centroid[0] /= points.length;
centroid[1] /= points.length;
centroid[2] /= points.length;
return new THREE.Vector3(centroid[0],centroid[1],centroid[2]);
}
For now ignoring the problem of getting the whole scene to fit within the camera (this is a common problem, I'm sure you can find useful information online (perhaps this?)).
Instead, let us focus on what seems to be your main question: You wish to find the center of a group of objects. What you are currently doing is you are computing an average of the object centers. This means that if you have one object to the far left, and 9 objects to the far right, your computed center point will also be far to the right. (This would be an approximation of the center of mass, assuming the objects are of similar mass.)
However, for the purpose of centering the camera so that every object is visible, you are not interested in the center of mass, but you wish to find a point such that the distance to the leftmost point is equal to the distance to the rightmost, and similarly the lowermost to the highermost, etc. Such a point can be found using the bounding box of all your objects. The center of this bounding box is the point you are looking for.
If your camera is to be aligned to the axes, you can easily compute such a bounding box for each object as follows:
new THREE.Box3().setFromObject(myMesh)
The bounding box of all the objects is simply the box represented by the lowest and highest coordinates of all the object bounding boxes you computed. The center of the complete bounding box will give you the point you are after.
Next, assuming the camera is aligned with the axes, the problem is simply finding a suitable distance from the camera to this point so that the entire bounding box fits inside the viewport.
So I have a very simple situation where the ground plane is basically where y=0 is and I want to keep the camera above the ground at all time. I also want good controls that feel intuitive (and are touch-compatible) and I figured out that the OrbitControls.js seems best suited for this, also because there I can easily limit the polar angle by setting maxPolarAngle to something less or equal Math.PI/2 (quarter turn).
Still, even doing that the user can pan below the ground unless panning is disabled altogether (which I don't want to). Manually limiting so that y never goes negative felt weird but I figured that simply ignoring any changed on the y axis by e.g. changing line 165 (panUp function) to the following
panOffset.set( te[ 4 ], 0, te[ 6 ] );
panOffset.normalize();
led to a quick and dirty solution that did about what I wanted.
But now the user can now only change the camera height by zooming.
Can you think of any better solution? Maybe this thread can serve as food for thought for an official solution, I am sure I wouldn't be the only one benefiting from such a solution.
/edit: I wasn't normalizing the offset vector at first which led to another issue, changed now.
Don't go below the ground:
controls.maxPolarAngle = Math.PI / 2
Just in case someone needs an answer:
You find the angle relative to the controls target and the ground position of the camera (regardless of altitude) and assign the maxPolarAngle. Adjust for your up axis, mine was Y. Inside the controls change event:
var centerPosition = controls.target.clone();
centerPosition.y = 0;
var groundPosition = camera.position.clone();
groundPosition.y = 0;
var d = (centerPosition.distanceTo(groundPosition));
var origin = new THREE.Vector2(controls.target.y,0);
var remote = new THREE.Vector2(0,d); // replace 0 with raycasted ground altitude
var angleRadians = Math.atan2(remote.y - origin.y, remote.x - origin.x);
controls.maxPolarAngle = angleRadians;
I had problems getting OrbitsControls.js to work as I desired. The soulution that worked best for me was to create my own CamaraControls that did function as per my requirements. I suspect this is not the answer your looking for as it involves a bit of work. You could always just add you own update function that clamps the values.
THREE.OrbitControls.clampedUpdate = function () {
THREE.OrbitControls.update.call(this);
if (this.object.position.y < 0) {
this.object.position.y = 0;
}
}
I've searched far and wide, so if there's a similar question please forgive me but I just couldn't find it.
To put what I'm trying to do in context: I want to create an infinitely-generated field of stars that disappear as they go offscreen and reappear at the edge of the screen where the camera is moving. I'm working with a top-down view, so it must be pretty simple to achieve this, but alas I haven't a clue.
I'm using the following code to determine whether a star has gone off-screen and then replace it:
//update camera frustum
camera.projScreenMatrix.multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
);
camera.frustum.setFromMatrix(camera.projScreenMatrix);
//loop through stars
var stars=scene.stars.geometry.vertices;
for(var i=0;i<stars.length;i++) {
if(!camera.frustum.containsPoint(stars[i])) {
stars[i]=new THREE.Vector3(
// fill in the blank
);
scene.stars.geometry.verticesNeedUpdate=true;
}
}
Since I'm using a perspective camera, I know I'll need to somehow factor in camera.fov and other perspective elements, but as you can tell I'm no expert on the third dimension.
Assuming I have an angle or normalized vector telling me the direction the view is panning, how would I go about creating a vertex along the edge of the screen regardless of its Z position?
If I'm not clear enough, I'll be happy to clarify. Thanks.
I know this is an old question, but I came across it while looking for an answer and found a simple, trigonometry reliant method to get the left edge of the camera frustum, and I'm sharing it in case someone else might find it useful:
// Get half of the cameras field of view angle in radians
var fov = camera.fov / 180 * Math.PI / 2;
// Get the adjacent to calculate the opposite
// This assumes you are looking at the scene
var adjacent = camera.position.distanceTo( scene.position );
// Use trig to get the leftmost point (tangent = o / a)
var left = Math.tan( fov ) * adjacent * camera.aspect;
Basically, this gets the leftmost point, but if you don't multiply by the aspect ratio you should get a point in a circle around your camera frustum, so you could translate a point any direction away from the cameras focus and it would always be outside the frustum.
It works by assuming that the imaginary plane that is the camera is perpendicular to the line connecting the camera and its focus, so there is a straight angle. This should work if you want objects further away as well (so if you want them at a further point from the camera you just need to increase the distance between the focus and the camera).
Well, countless headaches and another question later, I've come up with a fairly makeshift answer. Just in case by some unlikely chance someone else has the same question, the following function plots a point on the scene relative to the camera's current view with whatever Z specified:
//only needs to be defined once
var projector=new THREE.Projector();
//input THREE.Vector3
function(vector) {
var z=vector.z;
vector.z=0;
projector.unprojectVector(vector,camera);
return camera.position.clone().add(
vector
.sub(camera.position)
.normalize()
.multiplyScalar(
-(camera.position.z-z)/vector.z
)
);
The x and y, in this case, both range from -1 to 1 for bottom-left to top-right. You can use position/window.Width and position/window.Height for extra precision (using mouse coordinates or what have you).
I'm working on a voxel game with three.js. For this, I need to generate as many chunk as required to fill the screen. Currently, I'm loading a circle of radius 20 around the player.
What is the simplest way to compute the exact range of chunks required to fill the camera frustrum and avoid computing invisible chunks ?
Every chunk has the exact same size (let's say we have a vector size with the correct value), and are at Y=0 (X and Z varying).
var frustum = new THREE.Frustum();
frustum.setFromMatrix( new THREE.Matrix4().multiply( camera.projectionMatrix, camera.matrixWorldInverse ) );
for (var i=0; i<objects.length; i++) {
objects[i].visible = frustum.intersectsObject( objects[i] );
}
only objects that are within camera frustum will be rendered
documented here
hope this helps you?