+bool Plane::contains(const Vec3 &v) const
+{
+ return dot(v, normal) <= 0.0;
+}
+
+float Plane::distance(const Vec3 &v) const
+{
+ return std::max(dot(v - pt, normal), 0.0f);
+}
+
+float Plane::signed_distance(const Vec3 &v) const
+{
+ return dot(v - pt, normal);
+}
+
+float Plane::distance_sq(const Vec3 &v) const
+{
+ float d = distance(v);
+ return d * d;
+}
+
+float Plane::signed_distance_sq(const Vec3 &v) const
+{
+ float d = distance(v);
+ return dot(v, normal) >= 0.0f ? d * d : -(d * d);
+}
+
+
+Disc::Disc()
+{
+ type = GOBJ_DISC;
+ radius = 1.0;
+}
+
+Disc::Disc(const Vec3 &pt, const Vec3 &normal, float rad)
+ : Plane(pt, normal)
+{
+ type = GOBJ_DISC;
+ radius = rad;
+}
+
+Disc::Disc(const Vec3 &normal, float dist, float rad)
+ : Plane(normal, dist)
+{
+ type = GOBJ_DISC;
+ radius = rad;
+}
+
+bool Disc::valid() const
+{
+ return radius >= 0.0f;
+}
+
+void Disc::invalidate()
+{
+ radius = -1;
+}
+
+bool Disc::intersect(const Ray &ray, HitPoint *hit) const
+{
+ HitPoint phit;
+ if(Plane::intersect(ray, &phit)) {
+ if(length_sq(phit.pos - pt) <= radius * radius) {
+ *hit = phit;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Disc::contains(const Vec3 &pt) const
+{
+ Vec3 pj = proj_point_plane(pt, *this);
+ return length_sq(pj - this->pt) <= radius * radius;
+}
+
+float Disc::distance_sq(const Vec3 &v) const
+{
+ return 0.0; // TODO
+}
+
+float Disc::signed_distance_sq(const Vec3 &v) const
+{
+ return 0.0; // TODO
+}
+
+
+Vec3 proj_point_plane(const Vec3 &pt, const Plane &plane)
+{
+ float dist = plane.signed_distance(pt);
+ return pt - plane.normal * dist;
+}
+
+// ---- bounding sphere calculations ----
+
+bool calc_bounding_sphere(Sphere *sph, const GeomObject *obj)
+{
+ if(!obj->valid()) {
+ sph->invalidate();
+ return true;
+ }
+
+ switch(obj->type) {
+ case GOBJ_SPHERE:
+ *sph = *(Sphere*)obj;
+ break;
+
+ case GOBJ_AABOX:
+ sph->center = (AABOX(obj)->min + AABOX(obj)->max) * 0.5;
+ sph->radius = length(AABOX(obj)->max - AABOX(obj)->min) * 0.5;
+ break;
+
+ case GOBJ_BOX:
+ sph->center = (BOX(obj)->min + BOX(obj)->max) * 0.5 + BOX(obj)->xform.get_translation();
+ sph->radius = length(BOX(obj)->max - BOX(obj)->min) * 0.5;
+ break;
+
+ case GOBJ_PLANE:
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool calc_bounding_sphere(Sphere *sph, const GeomObject *a, const GeomObject *b)
+{
+ Sphere bsa, bsb;
+
+ if(!calc_bounding_sphere(&bsa, a) || !calc_bounding_sphere(&bsb, b)) {
+ return false;
+ }
+
+ float dist = length(bsa.center - bsb.center);
+ float surf_dist = dist - (bsa.radius + bsb.radius);
+ float d1 = bsa.radius + surf_dist / 2.0;
+ float d2 = bsb.radius + surf_dist / 2.0;
+ float t = d1 / (d1 + d2);
+
+ if(t < 0.0) t = 0.0;
+ if(t > 1.0) t = 1.0;
+
+ sph->center = bsa.center * t + bsb.center * (1.0 - t);
+ sph->radius = std::max(dist * t + bsb.radius, dist * (1.0f - t) + bsa.radius);
+ return true;
+}
+
+bool calc_bounding_sphere(Sphere *sph, const GeomObject **objv, int num)