-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathQuadtree.java
More file actions
190 lines (173 loc) · 5.09 KB
/
Quadtree.java
File metadata and controls
190 lines (173 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
* Quadtree.java
* Created by: William Tyas, portions credited to Geoffrey Matthews
* Date: 8/9/17
* Description: Used to speed up raytracing by dividing image plane
* into quadrants and checking if spheres reside in a quadrant. If they
* don't, there is no need to shoot a ray through any pixel in that
* quadrant.
*/
import java.util.*;
public class Quadtree {
private float minX, minY, maxX, maxY;
private int level;
private Quadtree ll, lr, ul, ur;
private SphereList sphereList;
private float camZ;
public SphereList getSphereList() { return this.sphereList; }
public Quadtree(float minX, float minY, float maxX, float maxY, int level, float camZ) {
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
this.level = level;
this.camZ = camZ;
this.sphereList = null;
if (level == 0) { // Leaf
this.ll = null;
this.lr = null;
this.ul = null;
this.ur = null;
} else { // interior node
level--;
float medX = 0.5f * (minX + maxX);
float medY = 0.5f * (minY + maxY);
this.ll = new Quadtree(minX, minY, medX, medY, level, camZ);
this.lr = new Quadtree(medX, minY, maxX, medY, level, camZ);
this.ul = new Quadtree(minX, medY, medX, maxY, level, camZ);
this.ur = new Quadtree(medX, medY, maxX, maxY, level, camZ);
}
}
public void addSphere(Sphere s) {
if (this.level == 0) { // leaf
if (this.sphereList == null) {
this.sphereList = new SphereList(s, null);
} else {
this.sphereList = this.sphereList.add(s);
}
} else {
// Get bounding box
float medX = 0.5f * (this.minX + this.maxX);
float medY = 0.5f * (this.minY + this.maxY);
float cx = s.getCenter().getX();
float cy = s.getCenter().getY();
float cz = s.getCenter().getZ();
float r = s.getRadius();
float pz = this.camZ;
// x-extent
float aX = (float) Math.sqrt(sqr(cx) + sqr(cz - pz));
float thetaX = (float) Math.atan(r/aX);
float psiX = (float) Math.asin(cx/aX);
float phiX = psiX - thetaX;
float x1 = pz * (float) Math.tan(phiX);
float x2 = pz * (float) Math.tan(phiX + 2*thetaX);
// y-extent
float aY = (float) Math.sqrt(sqr(cy) + sqr(cz - pz));
float thetaY = (float) Math.atan(r/aY);
float psiY = (float) Math.asin(cy/aY);
float phiY = psiY - thetaY;
float y1 = pz * (float) Math.tan(phiY);
float y2 = pz * (float) Math.tan(phiY + 2*thetaY);
// Make sure x2 > x1 and y2 > y1
if (x1 > x2) {
float temp = x1;
x1 = x2;
x2 = temp;
}
if (y1 > y2) {
float temp = y1;
y1 = y2;
y2 = temp;
}
// Send down tree to bottom, adding sphere to each node it's in
if (y1 < medY) { // in bottom half
if (x1 < medX) { // in bottom left
ll.addSphere(s);
}
if (x2 >= medX) { // in bottom right
lr.addSphere(s);
}
}
if (y2 >= medY) { // in top half
if (x1 < medX) { // in top left
ul.addSphere(s);
}
if (x2 >= medX) { // in top right
ur.addSphere(s);
}
}
}
}
// Add spheres that could cast shadows on one another
public void addShadowSphere(Sphere s) {
if (this.level == 0) { // leaf
if (this.sphereList == null) {
this.sphereList = new SphereList(s, null);
} else {
this.sphereList = this.sphereList.add(s);
}
} else {
// Get bounding box
float medX = 0.5f * (this.minX + this.maxX);
float medY = 0.5f * (this.minY + this.maxY);
float r = s.getRadius();
// u2-extent
float a1 = s.getCenterShadow().getY() + r;
float a2 = s.getCenterShadow().getY() - r;
// u3-extent
float b1 = s.getCenterShadow().getZ() + r;
float b2 = s.getCenterShadow().getZ() - r;
// Make sure a2 > a1 and b2 > b1
if (a1 > a2) {
float temp = a1;
a1 = a2;
a2 = temp;
}
if (b1 > b2) {
float temp = b1;
b1 = b2;
b2 = temp;
}
// Send down tree to bottom, adding sphere to each node it's in
if (b1 < medY) { // in bottom half
if (a1 < medX) { // in bottom left
ll.addShadowSphere(s);
}
if (a2 >= medX) { // in bottom right
lr.addShadowSphere(s);
}
}
if (b2 >= medY) { // in top half
if (a1 < medX) { // in top left
ul.addShadowSphere(s);
}
if (a2 >= medX) { // in top right
ur.addShadowSphere(s);
}
}
}
}
// Return spheres that intersect a given point on the screen
public SphereList getSpheres(float x, float y) {
// Go down tree to leaves to find list of spheres
if (this.level == 0) {
return this.sphereList;
}
float medX = 0.5f * (this.minX + this.maxX);
float medY = 0.5f * (this.minY + this.maxY);
if ((this.minX <= x) && (x < medX)) { // left half
if ((this.minY <= y) && (y < medY)) { // bottom half
return ll.getSpheres(x, y);
} else {
return ul.getSpheres(x, y);
}
} else { // right half
if ((this.minY <= y) && (y < medY)) { // bottom half
return lr.getSpheres(x, y);
} else {
return ur.getSpheres(x, y);
}
}
}
private float sqr(float x) { return x * x; }
}