+ float a, b, c, d, sqrt_d, q, t, t0, t1;
+ cgm_vec3 org, dir;
+ struct sph_data *sdata = (struct sph_data*)surf->data;
+
+ if(surf->node) {
+ erb_xform_ray(ray, surf->node->inv_xform, &org, &dir);
+ } else {
+ org = ray->o;
+ dir = ray->d;
+ }
+
+ a = cgm_vdot(&dir, &dir);
+ b = 2.0f * cgm_vdot(&org, &dir);
+ c = cgm_vdot(&org, &org) - sdata->rad * sdata->rad;
+
+ d = b * b - 4.0f * a * c;
+ if(d <= 0.0f) return 0;
+
+ sqrt_d = sqrt(d);
+ q = b < 0.0f ? -0.5f * (b - sqrt_d) : -0.5f * (b + sqrt_d);
+
+ t0 = q / a;
+ t1 = c / q;
+ if(t1 < t0) {
+ float ttmp = t0;
+ t0 = t1;
+ t1 = ttmp;
+ }
+
+ if(t0 > ray->tmax || t1 < ray->tmin) {
+ return 0;
+ }
+ t = t0 < ray->tmin ? t1 : t0;
+ if(t > ray->tmax) {
+ return 0;
+ }
+
+ if(hit) {
+ hit->t = t;
+ hit->err = 5e-4f * t; /* PBR 3.2.6 */
+ hit->surf = surf;
+ hit->pos = ray->o;
+ cgm_vadd_scaled(&hit->pos, &ray->d, t);
+ hit->lpos = org;
+ cgm_vadd_scaled(&hit->lpos, &dir, t);
+ hit->total_dist = ray->total_dist + t;
+ }
+
+ return 1;
+}
+
+static void sph_hitgeom(struct erb_surf *surf, struct erb_hit *hit)
+{
+ float theta, phi;
+
+ hit->norm = hit->lpos;
+ cgm_vnormalize(&hit->norm);
+
+ cgm_uvec_to_sph(&theta, &phi, &hit->norm);
+
+ hit->u = (theta + M_PI) / (2.0 * M_PI);
+ hit->v = phi / M_PI;
+
+ hit->tang.x = hit->norm.z;
+ hit->tang.y = hit->norm.y;
+ hit->tang.z = -hit->norm.x;
+
+ if(surf->node) {
+ cgm_vmul_m3v3(&hit->norm, surf->node->xform);
+ cgm_vmul_m3v3(&hit->tang, surf->node->xform);
+ }