I am currently trying to write a 2D only 3D "renderer" that uses polygons only.
Before rendering, I order the polygons by calculating one Z value as following:
double z = (v1.getZ()+v2.getZ()+v3.getZ());
My polygons consist of each three vectors (X,Y,Z)
Then I sort the polygons so I then can use the painters algorithm approach:
int i = 0;
for (Poly polygon : polyZSort) {
if(polygon.getRealZ()>z) {
break;
}
i++;
}
polyZSort.add(i, polygon);
The rendering then is as simple as:
for (Poly poly : polyZSort) {
OVector v1 = poly.getV1();
v1 = form.doTransform(v1);
OVector v2 = poly.getV2();
v2 = form.doTransform(v2);
OVector v3 = poly.getV3();
v3 = form.doTransform(v3);
g2d.setColor(poly.getColor());
Polygon pl = new Polygon();
pl.addPoint(getX(v1.getX()),getY(v1.getY()));
pl.addPoint(getX(v2.getX()),getY(v2.getY()));
pl.addPoint(getX(v3.getX()),getY(v3.getY()));
g2d.fillPolygon(pl);
// black border line
g2d.setColor(Color.BLACK);
g2d.drawLine(
getX(v1.getX()),
getY(v1.getY()),
getX(v2.getX()),
getY(v2.getY())
);
g2d.drawLine(
getX(v2.getX()),
getY(v2.getY()),
getX(v3.getX()),
getY(v3.getY())
);
g2d.drawLine(
getX(v3.getX()),
getY(v3.getY()),
getX(v1.getX()),
getY(v1.getY())
);
}
This works, even with multiple cubes (but only most of the time):
However in certain positions, the ordering seems to be wrong:
Does anybody know where the problem may be?
Here a video of the "glitch" https://vimeo.com/552355610
You are missing important steps ... This is how it should be done:
input is list of 3D triangles, output is list of 3D triangles
The output list is empty at start
"2D" screen project each processed triangle and leave it with original z values
so its still 3D , but x,y are screen positions and z is "original" z value in camera coordinate system.
check each processed triangle after projection if it intersects any triangle already in output list.
If no intersection occurs then simply add this triangle to output list as is.
if it does intersect you need to re-triangulate all intersected and new triangle so no intersections occur. And add these new triangle(s) to output list (while removing the original intersecting triangles from output list).
Here exampel of 2 overlaping triangles:
as you can see it can create quite a lot of new triangles and usually new triangle intersects more than just one triangle so you need to do this recursively for each triangle of the overlapped parts or create a list of all intersecting triangles first and retriangulate them at once ...
after whole scene is processed Z sort the output list by mid point z value
this step you already got.
render the sorted output list using 2D triangle rendering
this step you already got.
As you can see you need a buffer of size depending on number of triangles in scene and overlaps. Also this method is very complicated (and not very good choice for rookie programmers) and usable only for very simple scenes. For more complex ones is much easier and faster to use Z buffer. However that one require along with the buffer itself also 3D rendering of triangle (so one more interpolation and per pixel condition). As you can see its O(1) instead of the O(n.log(n)) for z sort.
Related
I am working on a C++ problem where I'm trying to make a utility function that takes as input two line segments starting points in 3d space [(x,y,z) and radius r]. If the segments can be oriented such that they end at the same point, the function should return true and print out that point. If there are multiple orientations that would produce a common endpoint, the function should choose the one that is furthest in the direction indicated by hint_direction.
The function receives these values:
bool func(
point3d position_0, // origin of first line segment.
float length_0, // length of first line segment.
point3d position_1, // origin of second line segment.
float length_1, // length of second line segment.
vector3d hint_direction, // in the event there are multiple solutions, return the one furthest in this direction.
point3d *out_common_end_position) // if result is true, point where both line segments can be oriented to end. otherwise uninitialized.
I have been following some guides online which lay out how to do this such as this: https://gamedev.stackexchange.com/questions/75756/sphere-sphere-intersection-and-circle-sphere-intersection.
I was able to successfully get all the intersecting points, but I cannot figure out how to get a point that is furthest in the hint direction.
I thought I might be able to use the intersection point between circle and hint_direction and get a angle of that point, but I cannot figure out how to do so.
As Spektre correctly pointed out I missed the 3D portion of your question, so the 4 options are the following:
no intersection (or one sphere completely lies within the other)
a single point (spheres touch from inside or outside)
a normal intersection forming a circle
both spheres overlap completely, i.e. they have the same origin and the same radius
Since the normal intersection would form a circle you'd want to project the direction hint onto that circle and calculate the intersection between the circle and the projected vector to get the farthest intersection point.
I have a rectangle Object with x, y, width and height. I have a list of these rectangles which are displayed on a screen. It is guaranteed that none of them overlap. Given a user's click position (x and y coordinates), I want to see which of these rectangles were clicked (since they do not overlap, there is a maximum of one rect that can be clicked).
I can obviously look through all of them and check for each one if the user clicked it but this is very slow because there are many on the screen. I can use some kind of comparison to keep the rectangles sorted when I insert a new one into the list. Is there some way to use something similar to binary search in order to decrease the time it takes to find which rect was clicked?
Note: the rectangles can be any size.
Thanks:)
Edit: To get an idea of what I am making visit koalastothemax.com
It highly depends upon your application and details we're not quite aware of yet for what the best solution would be. BUT, with as little as I know, I'd say you can make a 2D array that points to your rectangles. That 2D array would map directly to the pixels on the screen. So if you make the array 10x20, then the coordinate x divided by screen width times 10 (casted to int) will be the first index and y divided screen height times 20 would be your y index. With your x and y index, you can map directly to the rectangle that it points to. Some indexes might be empty and some might point to more than one rectangle if they're not perfectly laid out, but that seems the easiest way to me without knowing much about the application.
I have tackled a very similar problem in the past when developing a simulation. In my case the coordinates were doubles (so no integer indexing was possible) and there could be hundreds of millions of them that needed to be searched.
My solution was to create an Axis class to represent each axis as a sequence of ranges. The ranges were guaranteed to go from a minimum to a maximum and the class was smart enough to split itself into pieces when new ranges were added. Each range has a single generic object stored. The class used a binary search to find a range quickly.
So roughly the class looks like:
class Axis<T> {
public Axis(double min, double max, Supplier<T> creator);
public Stream<T> add(double from, double to);
public T get(double coord);
}
The add method needs to return a stream because the added range may cover several ranges.
To store rectanges:
Axis<Axis<Rectangle>> rectanges = new Axis<>(0.0, 100.0,
() -> new Axis<>(0.0, 100.0, Rectangle::new));
rectangles.add(x, x + w).forEach(r -> r.add(y, y + h).forEach(Rectangle::setPresent));
And to find a rectangle:
rectangles.get(x).get(y);
Note that there's always an object stored so you need a representation such as Rectangle.NULL for 'not present'. Or you could make it Optional<Rectangle> (though that indirection eats a lot of memory and processing for large numbers of rectangles).
I've just given the high level design here rather than any implementation details so let me know if you want more info on how to make it work. Getting the logic right on the range splits is not trivial. But I can guarantee that it's very fast even with very large numbers of rectangles.
The fastest way I can come up with is definitely not the most memory efficient. This works by exploiting the fact that an amortized hash table has constant lookup time. It will map every point that a rectangle has to that rectangle. This is only really effective if your are using integers. You might be able to get it to work with floats if you use a bit of rounding.
Make sure that the Point class has a hash code and equals function.
public class PointCheck
{
public Map<Point, Rect> pointMap;
public PointCheck()
{
pointMap = new HashMap<>();
}
/**
* Map all points that contain the rectangle
* to the rectangle.
*/
public void addRect(Rect rect)
{
for(int i = rect.x; i < rect.x + rect.width; ++i)
{
for(int j = rect.y; j < rect.y + rect.height; ++i)
{
pointMap.put(new Point(i, j), rect);
}
}
}
/**
* Returns the rectangle clicked, null
* if there is no rectangle.
*/
public Rect checkClick(Point click)
{
return pointMap.get(click);
}
}
Edit:
Just thought I should mention this: All of the rectangles held in the value of the hash map are references to the original rectangle, they are not clones.
I have put myself in a conundrum here, where I have 50 rects in one array and 50 rects in another. I need to find the two rects that are the closest to each other.
So I use this code:
for(int i=0;i<49;i++)
{
for(int j=0;j<49;j++)
{
double distance = Math.sqrt(Math.pow(rectF1.get(i).centerX() - rectF2.get(j).centerX(), 2)
+ Math.pow(rectF1.get(i).centerY() - rectF2.get(j).centerY(), 2));
}
}
It works but I have to check through 2500 times! And if the rects move (they do) then it's possible I won't catch the closest rects at the right moment! X.X
You seem to look base your search on the center of the rectangle. Thus you problem becomes a Nearest neighbour problem. To solve this, I would use a KD-Tree:
http://en.wikipedia.org/wiki/K-d_tree
For a java library look at KDTree Implementation in Java
In Java SE 7, I'm trying to solve a problem where I have a series of Rectangles. Through some user interaction, I get a Point. What I need to do is find the (first) Rectangle which contains the Point (if any).
Currently, I'm doing this via the very naieve solution of just storing the Rectangles in an ArrayList, and searching for the containing Rectangle by iterating over the list and using contains(). The problem is that, because this needs to be interactive for the user, this technique starts to be too slow for even a relatively small number of Rectangles (say, 200).
My current code looks something like this:
// Given rects is an ArrayList<Rectangle>, and p is a Point:
for(Rectangle r : rects)
{
if(r.contains(p))
{
return r;
}
}
return null;
Is there a more clever way to solve this problem (namely, in O(log n) instead of O(n), and/or with fewer calls to contains() by eliminating obviously bad candidates early)?
Yes, there is. Build 2 interval trees which will tell you if there is a rectangle between x1 to x2 and between y1 and y2. Then, when you have the co-ordinates of the point, perform O(log n) searches in both the trees.
That'll tell you if there are possibly rectangles around the point of interest. You still need to check if there is a common rectangle given by the two trees.
I'm trying to implement .3ds importer according to this documentation and I've approached the stage when I need to calculate vertex normals because .3ds files do not provide such. Here is the Java code:
/* Sctructure of vertex array is {x0, y0, z0, x1, y1, z1...}
*
* Basically, MathUtils.generateNormal_f(x0,y0,z0, x1,y1,z1, x2,y2,z2) is cross
* product between (x1-x0, y1-y0, z1-z0) and (x2-x0, y2-y0, z2-z0) */
normals = new float[this.vertex.length]; //every vertex has it's own normal
int n = 0;
for (int i=0; i<this.index.length; i++){
float[] Normal = MathUtils.generateNormal_f( //getting xyz coords of 1 normal
vertex[index[i]*3], vertex[index[i]*3+1], vertex[index[i]*3+2],
vertex[index[++i]*3], vertex[index[i]*3+1], vertex[index[i]*3+2],
vertex[index[++i]*3], vertex[index[i]*3+1], vertex[index[i]*3+2]);
normals[n++] = Normal[0];
normals[n++] = Normal[1];
normals[n++] = Normal[2];
}
Method MathUtils.generateNormal_f(...) tested and works fine. Result of this code can be seen below (first image). Just for example, in the second image, every normal of the model is the same and pointing towards the source of light.
Question is: how to calculate normals properly?
Your normals might be inverted.
I do not remember the 3ds format very well, but check if you can export and import the normals from the file instead of calculating them.
P.S. also do not use magic like this:
vertex[index[i]*3], vertex[index[i]*3+1], vertex[index[i]*3+2],
vertex[index[++i]*3], vertex[index[i]*3+1], vertex[index[i]*3+2],
vertex[index[++i]*3], vertex[index[i]*3+1], vertex[index[i]*3+2]
You will get different results based on the sequence of argument evaluation. better explicitly use [i], [i+1], [i+2] when calling calculate normal...
This information is correct as far as I know, and it's worked for me. For any 3 points A, B, and C on a plane, the normal, if we start at A, then B, and finally C, will be:
Where (B - A) and (C - B) each subtract two vectors, and the X sign represents finding the cross product of two vectors. The order of the points is quite important and determines our normal direction. If A, B, and C are organized in a counter-clockwise direction, than their normal will face outside the solid. If you want to know what a cross product is, then for any point P and Q, their cross product would be:
Another thing that is often done to the normal vector is that it is normalized. What this does is make the magnitude of the normal vector equal to 1 so that it is easier to work with. here's the equation:
Where the dot represents a dot product. If you don't know what a dot product is, allow me to illustrate by the following. For any points P and Q, their dot product, which is a scalar value, is:
Now that you have the surface normals, you can properly calculate the vertex normals for each vertex by averaging out the normals of any surface which shares that vertex. I don't have that formula on me, but I do know there are two approaches to find the vertex normal: weighted and non-weighted. A weighted approach involves calculating the area of each surface, while a non-weighted approach does not.
Hopefully, this information will help you. I leave the rest up to you or anyone else, as the remaining information is beyond my realm. Perhaps I'll come back and research some more on this question.