Panda3D
|
00001 // Filename: fisheyeMaker.cxx 00002 // Created by: drose (3Oct05) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "fisheyeMaker.h" 00016 #include "geomNode.h" 00017 #include "geom.h" 00018 #include "geomTristrips.h" 00019 #include "geomVertexWriter.h" 00020 #include "geomVertexFormat.h" 00021 #include "geomVertexArrayFormat.h" 00022 #include "internalName.h" 00023 #include "luse.h" 00024 #include "cmath.h" 00025 #include "mathNumbers.h" 00026 #include "graphicsStateGuardian.h" 00027 #include "displayRegion.h" 00028 00029 //////////////////////////////////////////////////////////////////// 00030 // Function: FisheyeMaker::reset 00031 // Access: Public 00032 // Description: Resets all the parameters to their initial defaults. 00033 //////////////////////////////////////////////////////////////////// 00034 void FisheyeMaker:: 00035 reset() { 00036 set_fov(360.0); 00037 _num_vertices = 1000; 00038 _square_inscribed = false; 00039 _square_radius = 1.0f; 00040 set_reflection(false); 00041 } 00042 00043 //////////////////////////////////////////////////////////////////// 00044 // Function: FisheyeMaker::set_fov 00045 // Access: Public 00046 // Description: Specifies the field of view of the fisheye 00047 // projection. A sphere map will have a 360-degree 00048 // field of view (and this is the default). 00049 //////////////////////////////////////////////////////////////////// 00050 void FisheyeMaker:: 00051 set_fov(PN_stdfloat fov) { 00052 _fov = fov; 00053 _half_fov_rad = deg_2_rad(_fov * 0.5f); 00054 } 00055 00056 00057 //////////////////////////////////////////////////////////////////// 00058 // Function: FisheyeMaker::generate 00059 // Access: Public 00060 // Description: Generates a GeomNode that renders the specified 00061 // geometry. 00062 //////////////////////////////////////////////////////////////////// 00063 PT(PandaNode) FisheyeMaker:: 00064 generate() { 00065 // Get some system-imposed limits. 00066 int max_vertices_per_array = 100; 00067 int max_vertices_per_primitive = 10000; 00068 bool prefers_triangle_strips = true; 00069 00070 /* 00071 GraphicsStateGuardian *global_gsg = GraphicsStateGuardian::get_global_gsg(); 00072 if (global_gsg != (GraphicsStateGuardian *)NULL) { 00073 max_vertices_per_array = global_gsg->get_max_vertices_per_array(); 00074 max_vertices_per_primitive = global_gsg->get_max_vertices_per_primitive(); 00075 prefers_triangle_strips = global_gsg->prefers_triangle_strips(); 00076 } 00077 */ 00078 00079 // We will generate a rose of radius 1, with vertices approximately 00080 // evenly distributed throughout. 00081 00082 // Since we will have _num_vertices filling the circle, and the area 00083 // of a circle of radius 1 is A = pi*r^2 = pi, it follows that the 00084 // number of vertices per square unit is (_num_vertices / pi), and 00085 // thus the number of vertices per linear unit is the square root of 00086 // that. 00087 PN_stdfloat vertices_per_unit = csqrt(_num_vertices / MathNumbers::pi_f); 00088 PN_stdfloat two_pi = 2.0f * MathNumbers::pi_f; 00089 00090 // The rose will be made up of concentric rings, originating from 00091 // the center, to a radius of 1.0. 00092 int num_rings = (int)floor(vertices_per_unit + 0.5f); 00093 00094 CPT(GeomVertexFormat) format = GeomVertexFormat::register_format 00095 (new GeomVertexArrayFormat 00096 (InternalName::get_vertex(), 3, 00097 Geom::NT_stdfloat, Geom::C_point, 00098 InternalName::get_texcoord(), 3, 00099 Geom::NT_stdfloat, Geom::C_texcoord)); 00100 00101 PT(GeomVertexData) vdata = 00102 new GeomVertexData(get_name(), format, Geom::UH_static); 00103 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00104 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); 00105 00106 PT(Geom) geom = new Geom(vdata); 00107 PT(GeomPrimitive) tristrips = new GeomTristrips(Geom::UH_static); 00108 tristrips->set_shade_model(Geom::SM_uniform); 00109 00110 PT(GeomNode) geom_node = new GeomNode(get_name()); 00111 00112 int last_ring_size = 3; 00113 int last_ring_vertex = 0; 00114 PN_stdfloat last_r = 1.0f / (PN_stdfloat)num_rings; 00115 00116 // Make the first triangle. We actually make a one-triangle strip, 00117 // but that seems more sensible than making a single isolated 00118 // triangle. 00119 for (int vi = 0; vi < last_ring_size; ++vi) { 00120 add_vertex(vertex, texcoord, last_r, 00121 two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size); 00122 tristrips->add_vertex(vi); 00123 } 00124 // Actually, we need to add one more degenerate triangle to make it 00125 // an even-length tristrip. 00126 tristrips->add_vertex(2); 00127 tristrips->close_primitive(); 00128 00129 // Now make all of the rings. 00130 for (int ri = 1; ri < num_rings; ++ri) { 00131 PN_stdfloat r = (PN_stdfloat)(ri + 1) / (PN_stdfloat)num_rings; 00132 00133 // The circumference of a ring of radius r is 2*pi*r. 00134 PN_stdfloat c = two_pi * r; 00135 int ring_size = (int)floor(c * vertices_per_unit + 0.5f); 00136 00137 // Each ring must either have exactly the same number of vertices 00138 // as the previous ring, or exactly double. 00139 if (ring_size < last_ring_size * 2) { 00140 // This one will be the same. 00141 ring_size = last_ring_size; 00142 } else { 00143 // This one will be double. 00144 ring_size = last_ring_size * 2; 00145 } 00146 00147 if (vdata->get_num_rows() + ring_size > max_vertices_per_array) { 00148 // Too many vertices; we need to start a new VertexData. 00149 if (tristrips->get_num_vertices() != 0) { 00150 geom->add_primitive(tristrips); 00151 } 00152 if (geom->get_num_primitives() != 0) { 00153 if (prefers_triangle_strips) { 00154 geom_node->add_geom(geom); 00155 } else { 00156 geom_node->add_geom(geom->decompose()); 00157 } 00158 } 00159 00160 vdata = new GeomVertexData(get_name(), format, Geom::UH_static); 00161 vertex = GeomVertexWriter(vdata, InternalName::get_vertex()); 00162 texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord()); 00163 geom = new Geom(vdata); 00164 tristrips = new GeomTristrips(Geom::UH_static); 00165 tristrips->set_shade_model(Geom::SM_uniform); 00166 00167 // Now we need to re-make the previous ring in this VertexData. 00168 last_ring_vertex = 0; 00169 for (int vi = 0; vi < last_ring_size; ++vi) { 00170 add_vertex(vertex, texcoord, last_r, 00171 two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size); 00172 } 00173 } 00174 00175 // Now make this ring. 00176 int ring_vertex = vdata->get_num_rows(); 00177 for (int vi = 0; vi < ring_size; ++vi) { 00178 add_vertex(vertex, texcoord, r, 00179 two_pi * (PN_stdfloat)vi / (PN_stdfloat)ring_size); 00180 } 00181 00182 // Now draw the triangle strip to connect the rings. 00183 if (ring_size == last_ring_size) { 00184 // Exactly the same size ring. This one is easy. 00185 if ((ring_size + 1) * 2 > max_vertices_per_primitive) { 00186 // Actually, we need to subdivide the ring to fit within the 00187 // GSG's advertised limits. 00188 int piece_size = max_vertices_per_primitive / 2 - 1; 00189 int vi = 0; 00190 while (vi < ring_size) { 00191 int piece_end = min(ring_size + 1, piece_size + 1 + vi); 00192 for (int pi = vi; pi < piece_end; ++pi) { 00193 tristrips->add_vertex(last_ring_vertex + pi % last_ring_size); 00194 tristrips->add_vertex(ring_vertex + pi % ring_size); 00195 } 00196 tristrips->close_primitive(); 00197 vi += piece_size; 00198 } 00199 00200 } else { 00201 // We can fit the entire ring. 00202 if (tristrips->get_num_vertices() > 0 && 00203 tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) { 00204 geom->add_primitive(tristrips); 00205 tristrips = new GeomTristrips(Geom::UH_static); 00206 tristrips->set_shade_model(Geom::SM_uniform); 00207 } 00208 for (int vi = 0; vi < ring_size; ++vi) { 00209 tristrips->add_vertex(last_ring_vertex + vi); 00210 tristrips->add_vertex(ring_vertex + vi); 00211 } 00212 tristrips->add_vertex(last_ring_vertex); 00213 tristrips->add_vertex(ring_vertex); 00214 tristrips->close_primitive(); 00215 } 00216 00217 } else { 00218 // Exactly double size ring. This is harder; we can't make a 00219 // single tristrip that goes all the way around the ring. 00220 // Instead, we'll make an alternating series of four-triangle 00221 // strips and two-triangle strips around the ring. 00222 int vi = 0; 00223 while (vi < last_ring_size) { 00224 if (tristrips->get_num_vertices() + 10 > max_vertices_per_primitive) { 00225 geom->add_primitive(tristrips); 00226 tristrips = new GeomTristrips(Geom::UH_static); 00227 tristrips->set_shade_model(Geom::SM_uniform); 00228 } 00229 tristrips->add_vertex(ring_vertex + (vi * 2 + 1) % ring_size); 00230 tristrips->add_vertex(ring_vertex + (vi * 2 + 2) % ring_size); 00231 tristrips->add_vertex(last_ring_vertex + (vi + 1) % last_ring_size); 00232 tristrips->add_vertex(ring_vertex + (vi * 2 + 3) % ring_size); 00233 tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size); 00234 tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size); 00235 tristrips->close_primitive(); 00236 00237 tristrips->add_vertex(ring_vertex + (vi * 2 + 4) % ring_size); 00238 tristrips->add_vertex(ring_vertex + (vi * 2 + 5) % ring_size); 00239 tristrips->add_vertex(last_ring_vertex + (vi + 2) % last_ring_size); 00240 tristrips->add_vertex(last_ring_vertex + (vi + 3) % last_ring_size); 00241 tristrips->close_primitive(); 00242 00243 vi += 2; 00244 } 00245 } 00246 00247 last_ring_size = ring_size; 00248 last_ring_vertex = ring_vertex; 00249 last_r = r; 00250 } 00251 00252 if (_square_inscribed) { 00253 // Make one more "ring", which extends out to the edges of a squre. 00254 int ring_size = last_ring_size; 00255 00256 if (vdata->get_num_rows() + ring_size > max_vertices_per_array) { 00257 // Too many vertices; we need to start a new VertexData. 00258 if (tristrips->get_num_vertices() != 0) { 00259 geom->add_primitive(tristrips); 00260 } 00261 if (geom->get_num_primitives() != 0) { 00262 if (prefers_triangle_strips) { 00263 geom_node->add_geom(geom); 00264 } else { 00265 geom_node->add_geom(geom->decompose()); 00266 } 00267 } 00268 00269 vdata = new GeomVertexData(get_name(), format, Geom::UH_static); 00270 vertex = GeomVertexWriter(vdata, InternalName::get_vertex()); 00271 texcoord = GeomVertexWriter(vdata, InternalName::get_texcoord()); 00272 geom = new Geom(vdata); 00273 tristrips = new GeomTristrips(Geom::UH_static); 00274 tristrips->set_shade_model(Geom::SM_uniform); 00275 00276 // Now we need to re-make the previous ring in this VertexData. 00277 last_ring_vertex = 0; 00278 for (int vi = 0; vi < last_ring_size; ++vi) { 00279 add_vertex(vertex, texcoord, last_r, 00280 two_pi * (PN_stdfloat)vi / (PN_stdfloat)last_ring_size); 00281 } 00282 } 00283 00284 // Now make this ring. 00285 int ring_vertex = vdata->get_num_rows(); 00286 for (int vi = 0; vi < ring_size; ++vi) { 00287 add_square_vertex(vertex, texcoord, 00288 two_pi * (PN_stdfloat)vi / (PN_stdfloat)ring_size); 00289 } 00290 00291 // Now draw the triangle strip to connect the rings. 00292 if ((ring_size + 1) * 2 > max_vertices_per_primitive) { 00293 // Actually, we need to subdivide the ring to fit within the 00294 // GSG's advertised limits. 00295 int piece_size = max_vertices_per_primitive / 2 - 1; 00296 int vi = 0; 00297 while (vi < ring_size) { 00298 int piece_end = min(ring_size + 1, piece_size + 1 + vi); 00299 for (int pi = vi; pi < piece_end; ++pi) { 00300 tristrips->add_vertex(last_ring_vertex + pi % last_ring_size); 00301 tristrips->add_vertex(ring_vertex + pi % ring_size); 00302 } 00303 tristrips->close_primitive(); 00304 vi += piece_size; 00305 } 00306 00307 } else { 00308 // We can fit the entire ring. 00309 if (tristrips->get_num_vertices() > 0 && 00310 tristrips->get_num_vertices() + ring_size * 2 > max_vertices_per_primitive) { 00311 geom->add_primitive(tristrips); 00312 tristrips = new GeomTristrips(Geom::UH_static); 00313 tristrips->set_shade_model(Geom::SM_uniform); 00314 } 00315 for (int vi = 0; vi < ring_size; ++vi) { 00316 tristrips->add_vertex(last_ring_vertex + vi); 00317 tristrips->add_vertex(ring_vertex + vi); 00318 } 00319 tristrips->add_vertex(last_ring_vertex); 00320 tristrips->add_vertex(ring_vertex); 00321 tristrips->close_primitive(); 00322 } 00323 } 00324 00325 if (tristrips->get_num_vertices() != 0) { 00326 geom->add_primitive(tristrips); 00327 } 00328 if (geom->get_num_primitives() != 0) { 00329 if (prefers_triangle_strips) { 00330 geom_node->add_geom(geom); 00331 } else { 00332 geom_node->add_geom(geom->decompose()); 00333 } 00334 } 00335 00336 return geom_node.p(); 00337 } 00338 00339 //////////////////////////////////////////////////////////////////// 00340 // Function: FisheyeMaker::add_vertex 00341 // Access: Private 00342 // Description: Given a point defined by a radius and an angle in 00343 // radians, compute the 2-d coordinates for the vertex 00344 // as well as the 3-d texture coordinates, and add both 00345 // to the VertexData. 00346 //////////////////////////////////////////////////////////////////// 00347 void FisheyeMaker:: 00348 add_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord, 00349 PN_stdfloat r, PN_stdfloat a) { 00350 PN_stdfloat sina, cosa; 00351 csincos(a, &sina, &cosa); 00352 00353 // The 2-d point is just a point r units from the center of the 00354 // circle. 00355 LPoint3 point(r * cosa, 0.0f, r * sina); 00356 vertex.add_data3(point); 00357 00358 // The 3-d point is the same thing, bent through the third dimension 00359 // around the surface of a sphere to the point in the back. 00360 PN_stdfloat b = r * _half_fov_rad; 00361 if (b >= MathNumbers::pi_f) { 00362 // Special case: we want to stop at the back pole, not continue 00363 // around it. 00364 texcoord.add_data3(0, _reflect, 0); 00365 00366 } else { 00367 PN_stdfloat sinb, cosb; 00368 csincos(b, &sinb, &cosb); 00369 LPoint3 tc(sinb * cosa, cosb * _reflect, sinb * sina); 00370 texcoord.add_data3(tc); 00371 } 00372 } 00373 00374 //////////////////////////////////////////////////////////////////// 00375 // Function: FisheyeMaker::add_square_vertex 00376 // Access: Private 00377 // Description: Similar to add_vertex(), but it draws the vertex all 00378 // the way out to the edge of the square we are 00379 // inscribed within, and the texture coordinate is 00380 // always the back pole. 00381 // 00382 // This is just for the purpose of drawing the 00383 // inscribing square. 00384 //////////////////////////////////////////////////////////////////// 00385 void FisheyeMaker:: 00386 add_square_vertex(GeomVertexWriter &vertex, GeomVertexWriter &texcoord, 00387 PN_stdfloat a) { 00388 PN_stdfloat sina, cosa; 00389 csincos(a, &sina, &cosa); 00390 00391 // Extend the 2-d point to the edge of the square of the indicated 00392 // size. 00393 if (cabs(sina) > cabs(cosa)) { 00394 PN_stdfloat y = (sina > 0.0f) ? _square_radius : -_square_radius; 00395 PN_stdfloat x = y * cosa / sina; 00396 LPoint3 point(x, 0.0f, y); 00397 vertex.add_data3(point); 00398 00399 } else { 00400 PN_stdfloat x = (cosa > 0.0f) ? _square_radius : -_square_radius; 00401 PN_stdfloat y = x * sina / cosa; 00402 LPoint3 point(x, 0.0f, y); 00403 vertex.add_data3(point); 00404 } 00405 00406 texcoord.add_data3(0, _reflect, 0); 00407 } 00408