Panda3D
Loading...
Searching...
No Matches
collisionInvSphere.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file collisionInvSphere.cxx
10 * @author drose
11 * @date 2005-01-05
12 */
13
14#include "collisionInvSphere.h"
15#include "collisionSphere.h"
16#include "collisionLine.h"
17#include "collisionRay.h"
18#include "collisionSegment.h"
19#include "collisionHandler.h"
20#include "collisionEntry.h"
21#include "config_collide.h"
22#include "omniBoundingVolume.h"
23#include "datagram.h"
24#include "datagramIterator.h"
25#include "bamReader.h"
26#include "bamWriter.h"
27#include "nearly_zero.h"
28#include "geom.h"
29#include "geomTristrips.h"
30#include "geomVertexWriter.h"
31
32PStatCollector CollisionInvSphere::_volume_pcollector("Collision Volumes:CollisionInvSphere");
33PStatCollector CollisionInvSphere::_test_pcollector("Collision Tests:CollisionInvSphere");
34TypeHandle CollisionInvSphere::_type_handle;
35
36/**
37 *
38 */
39CollisionSolid *CollisionInvSphere::
40make_copy() {
41 return new CollisionInvSphere(*this);
42}
43
44/**
45 *
46 */
47PT(CollisionEntry) CollisionInvSphere::
48test_intersection(const CollisionEntry &) const {
49 report_undefined_from_intersection(get_type());
50 return nullptr;
51}
52
53/**
54 * Returns a PStatCollector that is used to count the number of bounding
55 * volume tests made against a solid of this type in a given frame.
56 */
59 return _volume_pcollector;
60}
61
62/**
63 * Returns a PStatCollector that is used to count the number of intersection
64 * tests made against a solid of this type in a given frame.
65 */
68 return _test_pcollector;
69}
70
71/**
72 *
73 */
74void CollisionInvSphere::
75output(std::ostream &out) const {
76 out << "invsphere, c (" << get_center() << "), r " << get_radius();
77}
78
79/**
80 *
81 */
82PT(BoundingVolume) CollisionInvSphere::
83compute_internal_bounds() const {
84 // An inverse sphere always has an infinite bounding volume, since
85 // everything outside the sphere is solid matter.
86 return new OmniBoundingVolume();
87}
88
89/**
90 *
91 */
92PT(CollisionEntry) CollisionInvSphere::
93test_intersection_from_sphere(const CollisionEntry &entry) const {
94 const CollisionSphere *sphere;
95 DCAST_INTO_R(sphere, entry.get_from(), nullptr);
96
97 CPT(TransformState) wrt_space = entry.get_wrt_space();
98 const LMatrix4 &wrt_mat = wrt_space->get_mat();
99
100 LPoint3 from_center = sphere->get_center() * wrt_mat;
101 LVector3 from_radius_v =
102 LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
103 PN_stdfloat from_radius = length(from_radius_v);
104
105 LPoint3 into_center = get_center();
106 PN_stdfloat into_radius = get_radius();
107
108 LVector3 vec = from_center - into_center;
109 PN_stdfloat dist2 = dot(vec, vec);
110 if (dist2 < (into_radius - from_radius) * (into_radius - from_radius)) {
111 // No intersection--the sphere is within the hollow.
112 return nullptr;
113 }
114
115 if (collide_cat.is_debug()) {
116 collide_cat.debug()
117 << "intersection detected from " << entry.get_from_node_path()
118 << " into " << entry.get_into_node_path() << "\n";
119 }
120 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
121
122 LVector3 surface_normal;
123 PN_stdfloat vec_length = vec.length();
124 if (IS_NEARLY_ZERO(vec_length)) {
125 // If we don't have a collision normal (e.g. the centers are exactly
126 // coincident), then make up an arbitrary normal--any one is as good as
127 // any other.
128 surface_normal.set(1.0, 0.0, 0.0);
129 } else {
130 surface_normal = vec / -vec_length;
131 }
132
133 LVector3 normal = (has_effective_normal() && sphere->get_respect_effective_normal()) ? get_effective_normal() : surface_normal;
134
135 new_entry->set_surface_normal(normal);
136 new_entry->set_surface_point(into_center - surface_normal * into_radius);
137 new_entry->set_interior_point(from_center - surface_normal * from_radius);
138
139 return new_entry;
140}
141
142/**
143 *
144 */
145PT(CollisionEntry) CollisionInvSphere::
146test_intersection_from_line(const CollisionEntry &entry) const {
147 const CollisionLine *line;
148 DCAST_INTO_R(line, entry.get_from(), nullptr);
149
150 CPT(TransformState) wrt_space = entry.get_wrt_space();
151 const LMatrix4 &wrt_mat = wrt_space->get_mat();
152
153 LPoint3 from_origin = line->get_origin() * wrt_mat;
154 LVector3 from_direction = line->get_direction() * wrt_mat;
155
156 double t1, t2;
157 if (!intersects_line(t1, t2, from_origin, from_direction, 0.0f)) {
158 // The line is in the middle of space, and therefore intersects the
159 // sphere.
160 t1 = t2 = 0.0;
161 }
162
163 if (collide_cat.is_debug()) {
164 collide_cat.debug()
165 << "intersection detected from " << entry.get_from_node_path()
166 << " into " << entry.get_into_node_path() << "\n";
167 }
168 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
169
170 LPoint3 into_intersection_point = from_origin + t2 * from_direction;
171 new_entry->set_surface_point(into_intersection_point);
172
174 new_entry->set_surface_normal(get_effective_normal());
175 } else {
176 LVector3 normal = into_intersection_point - get_center();
177 normal.normalize();
178 new_entry->set_surface_normal(-normal);
179 }
180
181 return new_entry;
182}
183
184/**
185 *
186 */
187PT(CollisionEntry) CollisionInvSphere::
188test_intersection_from_ray(const CollisionEntry &entry) const {
189 const CollisionRay *ray;
190 DCAST_INTO_R(ray, entry.get_from(), nullptr);
191
192 CPT(TransformState) wrt_space = entry.get_wrt_space();
193 const LMatrix4 &wrt_mat = wrt_space->get_mat();
194
195 LPoint3 from_origin = ray->get_origin() * wrt_mat;
196 LVector3 from_direction = ray->get_direction() * wrt_mat;
197
198 double t1, t2;
199 if (!intersects_line(t1, t2, from_origin, from_direction, 0.0f)) {
200 // The ray is in the middle of space, and therefore intersects the sphere.
201 t1 = t2 = 0.0;
202 }
203
204 t2 = std::max(t2, 0.0);
205
206 if (collide_cat.is_debug()) {
207 collide_cat.debug()
208 << "intersection detected from " << entry.get_from_node_path()
209 << " into " << entry.get_into_node_path() << "\n";
210 }
211 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
212
213 LPoint3 into_intersection_point;
214 into_intersection_point = from_origin + t2 * from_direction;
215 new_entry->set_surface_point(into_intersection_point);
216
218 new_entry->set_surface_normal(get_effective_normal());
219 } else {
220 LVector3 normal = into_intersection_point - get_center();
221 normal.normalize();
222 new_entry->set_surface_normal(-normal);
223 }
224
225 return new_entry;
226}
227
228/**
229 *
230 */
231PT(CollisionEntry) CollisionInvSphere::
232test_intersection_from_segment(const CollisionEntry &entry) const {
233 const CollisionSegment *segment;
234 DCAST_INTO_R(segment, entry.get_from(), nullptr);
235
236 CPT(TransformState) wrt_space = entry.get_wrt_space();
237 const LMatrix4 &wrt_mat = wrt_space->get_mat();
238
239 LPoint3 from_a = segment->get_point_a() * wrt_mat;
240 LPoint3 from_b = segment->get_point_b() * wrt_mat;
241 LVector3 from_direction = from_b - from_a;
242
243 double t1, t2;
244 if (!intersects_line(t1, t2, from_a, from_direction, 0.0f)) {
245 // The segment is in the middle of space, and therefore intersects the
246 // sphere.
247 t1 = t2 = 0.0;
248 }
249
250 double t;
251 if (t2 <= 0.0) {
252 // The segment is completely below the shell.
253 t = 0.0;
254
255 } else if (t1 >= 1.0) {
256 // The segment is completely above the shell.
257 t = 1.0;
258
259 } else if (t2 <= 1.0) {
260 // The bottom edge of the segment intersects the shell.
261 t = std::min(t2, 1.0);
262
263 } else if (t1 >= 0.0) {
264 // The top edge of the segment intersects the shell.
265 t = std::max(t1, 0.0);
266
267 } else {
268 // Neither edge of the segment intersects the shell. It follows that both
269 // intersection points are within the hollow center of the sphere;
270 // therefore, there is no intersection.
271 return nullptr;
272 }
273
274 if (collide_cat.is_debug()) {
275 collide_cat.debug()
276 << "intersection detected from " << entry.get_from_node_path()
277 << " into " << entry.get_into_node_path() << "\n";
278 }
279 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
280
281 LPoint3 into_intersection_point = from_a + t * from_direction;
282 new_entry->set_surface_point(into_intersection_point);
283
285 new_entry->set_surface_normal(get_effective_normal());
286 } else {
287 LVector3 normal = into_intersection_point - get_center();
288 normal.normalize();
289 new_entry->set_surface_normal(-normal);
290 }
291
292 return new_entry;
293}
294
295/**
296 *
297 */
298PT(CollisionEntry) CollisionInvSphere::
299test_intersection_from_capsule(const CollisionEntry &entry) const {
300 const CollisionCapsule *capsule;
301 DCAST_INTO_R(capsule, entry.get_from(), nullptr);
302
303 CPT(TransformState) wrt_space = entry.get_wrt_space();
304 const LMatrix4 &wrt_mat = wrt_space->get_mat();
305
306 LPoint3 from_a = capsule->get_point_a() * wrt_mat;
307 LPoint3 from_b = capsule->get_point_b() * wrt_mat;
308
309 LVector3 from_radius_v =
310 LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
311 PN_stdfloat from_radius = from_radius_v.length();
312
313 LPoint3 center = get_center();
314 PN_stdfloat radius = get_radius();
315
316 // Check which one of the points lies furthest inside the sphere.
317 PN_stdfloat dist_a = (from_a - center).length();
318 PN_stdfloat dist_b = (from_b - center).length();
319 if (dist_b > dist_a) {
320 // Store the furthest point into from_a/dist_a.
321 dist_a = dist_b;
322 from_a = from_b;
323 }
324
325 // from_a now contains the furthest point. Is it inside?
326 if (dist_a < radius - from_radius) {
327 return nullptr;
328 }
329
330 if (collide_cat.is_debug()) {
331 collide_cat.debug()
332 << "intersection detected from " << entry.get_from_node_path()
333 << " into " << entry.get_into_node_path() << "\n";
334 }
335 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
336
337 LVector3 normal = center - from_a;
338 normal.normalize();
339 new_entry->set_surface_point(get_center() - normal * radius);
340 new_entry->set_interior_point(from_a - normal * from_radius);
341
343 new_entry->set_surface_normal(get_effective_normal());
344 } else {
345 new_entry->set_surface_normal(normal);
346 }
347
348 return new_entry;
349}
350
351/**
352 * Double dispatch point for box as a FROM object
353 */
354PT(CollisionEntry) CollisionInvSphere::
355test_intersection_from_box(const CollisionEntry &entry) const {
356 const CollisionBox *box;
357 DCAST_INTO_R(box, entry.get_from(), nullptr);
358
359 CPT(TransformState) wrt_space = entry.get_wrt_space();
360 const LMatrix4 &wrt_mat = wrt_space->get_mat();
361
362 LPoint3 center = get_center();
363 PN_stdfloat radius_sq = get_radius();
364 radius_sq *= radius_sq;
365
366 // Just figure out which box point is furthest from the center. If it
367 // exceeds the radius, the furthest point wins.
368
369 PN_stdfloat max_dist_sq = -1.0;
370 LPoint3 deepest_vertex;
371
372 for (int i = 0; i < 8; ++i) {
373 LPoint3 point = wrt_mat.xform_point(box->get_point(i));
374
375 PN_stdfloat dist_sq = (point - center).length_squared();
376 if (dist_sq > max_dist_sq) {
377 deepest_vertex = point;
378 max_dist_sq = dist_sq;
379 }
380 }
381
382 if (max_dist_sq < radius_sq) {
383 // The point furthest away from the center is still inside the sphere.
384 // Therefore, no collision.
385 return nullptr;
386 }
387
388 if (collide_cat.is_debug()) {
389 collide_cat.debug()
390 << "intersection detected from " << entry.get_from_node_path()
391 << " into " << entry.get_into_node_path() << "\n";
392 }
393
394 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
395
396 // The interior point is just the deepest cube vertex.
397 new_entry->set_interior_point(deepest_vertex);
398
399 // Now extrapolate the surface point and normal from that.
400 LVector3 normal = center - deepest_vertex;
401 normal.normalize();
402 new_entry->set_surface_point(center - normal * get_radius());
403 new_entry->set_surface_normal(
405 ? get_effective_normal() : normal);
406
407 return new_entry;
408}
409
410/**
411 * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
412 * solid.
413 */
414void CollisionInvSphere::
415fill_viz_geom() {
416 if (collide_cat.is_debug()) {
417 collide_cat.debug()
418 << "Recomputing viz for " << *this << "\n";
419 }
420
421 static const int num_slices = 16;
422 static const int num_stacks = 8;
423
424 PT(GeomVertexData) vdata = new GeomVertexData
425 ("collision", GeomVertexFormat::get_v3(),
426 Geom::UH_static);
427 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
428
429 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
430 for (int sl = 0; sl < num_slices; ++sl) {
431 PN_stdfloat longitude0 = (PN_stdfloat)sl / (PN_stdfloat)num_slices;
432 PN_stdfloat longitude1 = (PN_stdfloat)(sl + 1) / (PN_stdfloat)num_slices;
433 vertex.add_data3(compute_point(0.0, longitude0));
434 for (int st = 1; st < num_stacks; ++st) {
435 PN_stdfloat latitude = (PN_stdfloat)st / (PN_stdfloat)num_stacks;
436 vertex.add_data3(compute_point(latitude, longitude1));
437 vertex.add_data3(compute_point(latitude, longitude0));
438 }
439 vertex.add_data3(compute_point(1.0, longitude0));
440
441 strip->add_next_vertices(num_stacks * 2);
442 strip->close_primitive();
443 }
444
445 PT(Geom) geom = new Geom(vdata);
446 geom->add_primitive(strip);
447
448 _viz_geom->add_geom(geom, get_solid_viz_state());
449}
450
451/**
452 * Factory method to generate a CollisionInvSphere object
453 */
456 BamReader::get_factory()->register_factory(get_class_type(), make_CollisionInvSphere);
457}
458
459/**
460 * Function to write the important information in the particular object to a
461 * Datagram
462 */
467
468/**
469 * Factory method to generate a CollisionInvSphere object
470 */
471TypedWritable *CollisionInvSphere::
472make_CollisionInvSphere(const FactoryParams &params) {
474 DatagramIterator scan;
475 BamReader *manager;
476
477 parse_params(params, scan, manager);
478 me->fillin(scan, manager);
479 return me;
480}
481
482/**
483 * Function that reads out of the datagram (or asks manager to read) all of
484 * the data that is needed to re-create this object and stores it in the
485 * appropiate place
486 */
487void CollisionInvSphere::
488fillin(DatagramIterator& scan, BamReader* manager) {
489 CollisionSphere::fillin(scan, manager);
490}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition bamReader.I:275
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition bamReader.h:110
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
LPoint3 get_point(int n) const
Returns the nth vertex of the OBB.
Defines a single collision event.
get_from_node_path
Returns the NodePath that represents the CollisionNode that contains the CollisionSolid that triggere...
get_into_node_path
Returns the NodePath that represents the specific CollisionNode or GeomNode instance that was collide...
get_from
Returns the CollisionSolid pointer for the particular solid that triggered this collision.
An inverted sphere: this is a sphere whose collision surface is the inside surface of the sphere.
static void register_with_read_factory()
Factory method to generate a CollisionInvSphere object.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
virtual PStatCollector & get_test_pcollector()
Returns a PStatCollector that is used to count the number of intersection tests made against a solid ...
virtual PStatCollector & get_volume_pcollector()
Returns a PStatCollector that is used to count the number of bounding volume tests made against a sol...
The abstract base class for all things that can collide with other things in the world,...
bool has_effective_normal() const
Returns true if a special normal was set by set_effective_normal(), false otherwise.
const LVector3 & get_effective_normal() const
Returns the normal that was set by set_effective_normal().
get_respect_effective_normal
See set_respect_effective_normal().
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
A class to retrieve the individual data elements previously stored in a Datagram.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
An instance of this class is passed to the Factory when requesting it to do its business and construc...
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition factory.I:73
static const GeomVertexFormat * get_v3()
Returns a standard vertex format with just a 3-component vertex position.
A lightweight class that represents a single element that may be timed and/or counted via stats.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
STTransform::operator CPT TransformState() const
This is used internally to convert an STTransform into a TransformState pointer.
Definition stTransform.I:98