Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Projector.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: Projector.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  3D Projection Camera class
13 */
14 
15 #include "MemDebug.h"
16 #include "Projector.h"
17 
18 // +--------------------------------------------------------------------+
19 
20 static const float CLIP_PLANE_EPSILON = 0.0001f;
21 static const double Z_NEAR = 1.0;
22 
23 void Print(const char* fmt, ...);
24 
25 // +--------------------------------------------------------------------+
26 
27 static Camera emergency_cam;
28 
29 // +--------------------------------------------------------------------+
30 
32 : camera(cam), infinite(0), depth_scale(1.0f), orthogonal(false), field_of_view(2)
33 {
34  if (!camera)
35  camera = &emergency_cam;
36 
37  UseWindow(window);
38 }
39 
41 { }
42 
43 // +--------------------------------------------------------------------+
44 
45 void
47 {
48  if (cam)
49  camera = cam;
50  else
51  camera = &emergency_cam;
52 }
53 
54 void
56 {
57  Rect r = win->GetRect();
58  width = r.w;
59  height = r.h;
60 
61  xcenter = (width / 2.0);
62  ycenter = (height / 2.0);
63 
64  xclip0 = 0.0f;
65  xclip1 = (float) width-0.5f;
66  yclip0 = 0.0f;
67  yclip1 = (float) height-0.5f;
68 
70 }
71 
72 void
74 {
75  field_of_view = fov;
76 
77  xscreenscale = width / fov;
78  yscreenscale = height / fov;
79 
81 
82  xangle = atan(2.0/fov * maxscale/xscreenscale);
83  yangle = atan(2.0/fov * maxscale/yscreenscale);
84 }
85 
86 double
88 {
89  return field_of_view;
90 }
91 
92 void
94 {
95  depth_scale = scale;
96 }
97 
98 double
100 {
101  return depth_scale;
102 }
103 
104 int
106 {
107  int old = infinite;
108  infinite = i;
109  return old;
110 }
111 
112 // +--------------------------------------------------------------------+
113 
114 void
116 {
117  SetUpFrustum();
118  SetWorldSpace();
119 }
120 
121 // +--------------------------------------------------------------------+
122 // Transform a point from worldspace to viewspace.
123 // +--------------------------------------------------------------------+
124 
125 void
127 {
128  Vec3 tvert = vec;
129 
130  // Translate into a viewpoint-relative coordinate
131  if (!infinite)
132  tvert -= camera->Pos();
133 
134  // old method:
135  vec.x = (tvert * camera->vrt());
136  vec.y = (tvert * camera->vup());
137  vec.z = (tvert * camera->vpn());
138 
139  // Rotate into the view orientation
140  // vec = tvert * camera->Orientation();
141 }
142 
143 // +--------------------------------------------------------------------+
144 // Transform a point from worldspace to viewspace.
145 // +--------------------------------------------------------------------+
146 
147 void
149 {
150  Point tvert = point;
151 
152  // Translate into a viewpoint-relative coordinate
153  if (!infinite)
154  tvert -= camera->Pos();
155 
156  // old method:
157  point.x = (tvert * camera->vrt());
158  point.y = (tvert * camera->vup());
159  point.z = (tvert * camera->vpn());
160 
161  // Rotate into the view orientation
162  // point = tvert * camera->Orientation();
163 }
164 
165 // +--------------------------------------------------------------------+
166 // APPARENT RADIUS OF PROJECTED OBJECT
167 // Project a viewspace point into screen coordinates.
168 // Use projected Z to determine apparent radius of object.
169 // +--------------------------------------------------------------------+
170 
171 float
172 Projector::ProjectRadius(const Vec3& v, float radius) const
173 {
174  return (float) fabs((radius * maxscale) / v.z);
175 }
176 
177 // +--------------------------------------------------------------------+
178 // IN PLACE PROJECTION OF POINT
179 // Project a viewspace point into screen coordinates.
180 // Note that the y axis goes up in worldspace and viewspace, but
181 // goes down in screenspace.
182 // +--------------------------------------------------------------------+
183 
184 void
186 {
187  double zrecip;
188 
189  if (orthogonal) {
190  double scale = field_of_view/2;
191  v.x = (float) (xcenter + scale * v.x);
192  v.y = (float) (height - (ycenter + scale * v.y));
193  v.z = (float) (0.0f);
194  }
195 
196  else {
197  //zrecip = 2 * (1.0e5 / (1.0e5-1)) / v.z;
198  //zrecip = 2 * 0.97 / v.z; -- what the heck was this version used for?
199 
200  zrecip = 2 / v.z;
201  v.x = (float) (xcenter + maxscale * v.x * zrecip);
202  v.y = (float) (height - (ycenter + maxscale * v.y * zrecip));
203  v.z = (float) (1 - zrecip);
204  }
205 
206  // clamp the point to the viewport:
207  if (clamp) {
208  if (v.x < xclip0) v.x = xclip0;
209  if (v.x > xclip1) v.x = xclip1;
210  if (v.y < yclip0) v.y = yclip0;
211  if (v.y > yclip1) v.y = yclip1;
212  }
213 }
214 
215 // +--------------------------------------------------------------------+
216 // IN PLACE PROJECTION OF POINT
217 // Project a viewspace point into screen coordinates.
218 // Note that the y axis goes up in worldspace and viewspace, but
219 // goes down in screenspace.
220 // +--------------------------------------------------------------------+
221 
222 void
224 {
225  double zrecip;
226 
227  if (orthogonal) {
228  double scale = field_of_view/2;
229  v.x = (xcenter + scale * v.x);
230  v.y = (height - (ycenter + scale * v.y));
231  v.z = 0;
232  }
233 
234  else {
235  zrecip = 1 / v.z;
236  v.x = (xcenter + 2 * maxscale * v.x * zrecip);
237  v.y = (height - (ycenter + 2 * maxscale * v.y * zrecip));
238  v.z = (1 - zrecip);
239  }
240 
241  // clamp the point to the viewport:
242  if (clamp) {
243  if (v.x < xclip0) v.x = xclip0;
244  if (v.x > xclip1) v.x = xclip1;
245  if (v.y < yclip0) v.y = yclip0;
246  if (v.y > yclip1) v.y = yclip1;
247  }
248 }
249 
250 // +--------------------------------------------------------------------+
251 // IN PLACE UN-PROJECTION OF POINT
252 // Convert a point in screen coordinates back to viewspace.
253 // Note that the y axis goes up in worldspace and viewspace, but
254 // goes down in screenspace.
255 // +--------------------------------------------------------------------+
256 
257 void
259 {
260  double zrecip = 1 / v.z;
261 
262  /***
263  * forward projection:
264 v.x = (xcenter + maxscale * v.x * zrecip);
265 v.y = (height - (ycenter + maxscale * v.y * zrecip));
266 v.z = (1 - zrecip);
267 ***/
268 
269  v.x = ( v.x - xcenter) / (maxscale * zrecip);
270  v.y = (height - v.y - ycenter) / (maxscale * zrecip);
271 }
272 
273 // +--------------------------------------------------------------------+
274 // IN PLACE PROJECTION OF RECTANGLE (FOR SPRITES)
275 // Project a viewspace point into screen coordinates.
276 // Note that the y axis goes up in worldspace and viewspace, but
277 // goes down in screenspace.
278 // +--------------------------------------------------------------------+
279 
280 void
281 Projector::ProjectRect(Point& v, double& w, double& h) const
282 {
283  double zrecip;
284 
285  if (orthogonal) {
286  double scale = field_of_view/2;
287  v.x = (xcenter + scale * v.x);
288  v.y = (height - (ycenter + scale * v.y));
289  v.z = 0;
290  }
291 
292  else {
293  zrecip = 1 / v.z;
294  v.x = (xcenter + 2 * maxscale * v.x * zrecip);
295  v.y = (height - (ycenter + 2 * maxscale * v.y * zrecip));
296  v.z = (1 - Z_NEAR*zrecip);
297 
298  w *= maxscale * zrecip;
299  h *= maxscale * zrecip;
300  }
301 }
302 
303 // +--------------------------------------------------------------------+
304 // Set up a clip plane with the specified normal.
305 // +--------------------------------------------------------------------+
306 
307 void
309 {
310  // Rotate the plane normal into worldspace
311  ViewToWorld(normal, plane.normal);
312  plane.distance = (float) (camera->Pos() * plane.normal + CLIP_PLANE_EPSILON);
313 }
314 
315 // +--------------------------------------------------------------------+
316 // Set up the planes of the frustum, in worldspace coordinates.
317 // +--------------------------------------------------------------------+
318 
319 void
321 {
322  double angle, s, c;
323  Vec3 normal;
324 
325  angle = XAngle();
326  s = sin(angle);
327  c = cos(angle);
328 
329  // Left clip plane
330  normal.x = (float) s;
331  normal.y = (float) 0;
332  normal.z = (float) c;
333  view_planes[0].normal = normal;
334  view_planes[0].distance = CLIP_PLANE_EPSILON;
336 
337  // Right clip plane
338  normal.x = (float) -s;
339  view_planes[1].normal = normal;
340  view_planes[1].distance = CLIP_PLANE_EPSILON;
342 
343  angle = YAngle();
344  s = sin(angle);
345  c = cos(angle);
346 
347  // Bottom clip plane
348  normal.x = (float) 0;
349  normal.y = (float) s;
350  normal.z = (float) c;
351  view_planes[2].normal = normal;
352  view_planes[2].distance = CLIP_PLANE_EPSILON;
354 
355  // Top clip plane
356  normal.y = (float) -s;
357  view_planes[3].normal = normal;
358  view_planes[3].distance = CLIP_PLANE_EPSILON;
360 }
361 
362 // +--------------------------------------------------------------------+
363 // Clip the point against the frustum and return 1 if partially inside
364 // Return 2 if completely inside
365 // +--------------------------------------------------------------------+
366 
367 int
368 Projector::IsVisible(const Vec3& v, float radius) const
369 {
370  int visible = 1;
371  int complete = 1;
372 
373  Plane* plane = (Plane*) frustum_planes;
374  if (infinite) {
375  complete = 0;
376 
377  for (int i = 0; visible && (i < NUM_FRUSTUM_PLANES); i++) {
378  visible = ((v * plane->normal) >= CLIP_PLANE_EPSILON);
379  plane++;
380  }
381  }
382  else {
383  for (int i = 0; visible && (i < NUM_FRUSTUM_PLANES); i++) {
384  float dot = v * plane->normal;
385  visible = ((dot + radius) >= plane->distance);
386  complete = complete && ((dot - radius) >= plane->distance);
387  plane++;
388  }
389  }
390 
391  return visible + complete;
392 }
393 
394 // +--------------------------------------------------------------------+
395 // Clip the bouding point against the frustum and return non zero
396 // if at least partially inside. This version is not terribly
397 // efficient as it checks all eight box corners rather than just
398 // the minimum two.
399 // +--------------------------------------------------------------------+
400 
401 int
403 {
404  int i, j, outside = 0;
405 
406  // if all eight corners are outside of the same
407  // frustrum plane, then the box is not visible
408  Plane* plane = (Plane*) frustum_planes;
409 
410  if (infinite) {
411  for (i = 0; !outside && (i < NUM_FRUSTUM_PLANES); i++) {
412  for (j = 0; j < 8; j++)
413  outside += (p[j] * plane->normal) < CLIP_PLANE_EPSILON;
414 
415  if (outside < 8)
416  outside = 0;
417 
418  plane++;
419  }
420  }
421  else {
422  for (i = 0; !outside && (i < NUM_FRUSTUM_PLANES); i++) {
423  for (j = 0; j < 8; j++)
424  outside += (p[j] * plane->normal) < plane->distance;
425 
426  if (outside < 8)
427  outside = 0;
428 
429  plane++;
430  }
431  }
432 
433  // if not outside, then the box is visible
434  return !outside;
435 }
436 
437 // +--------------------------------------------------------------------+
438 
439 float
440 Projector::ApparentRadius(const Vec3& v, float radius) const
441 {
442  Vec3 vloc = v;
443 
444  Transform(vloc); // transform in place
445  return ProjectRadius(vloc, radius);
446 }
447 
448 
449 // +--------------------------------------------------------------------+
450 // Rotate a vector from viewspace to worldspace.
451 // +--------------------------------------------------------------------+
452 
453 void
455 {
456  // Rotate into the world orientation
457  pout.x = pin.x * camera->vrt().x + pin.y * camera->vup().x + pin.z * camera->vpn().x;
458  pout.y = pin.x * camera->vrt().y + pin.y * camera->vup().y + pin.z * camera->vpn().y;
459  pout.z = pin.x * camera->vrt().z + pin.y * camera->vup().z + pin.z * camera->vpn().z;
460 }
461 
462 void
464 {
465  // Rotate into the world orientation
466  vout.x = (float) (vin.x * camera->vrt().x + vin.y * camera->vup().x + vin.z * camera->vpn().x);
467  vout.y = (float) (vin.x * camera->vrt().y + vin.y * camera->vup().y + vin.z * camera->vpn().y);
468  vout.z = (float) (vin.x * camera->vrt().z + vin.y * camera->vup().z + vin.z * camera->vpn().z);
469 }
470