/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "core/geometry_bih.h" #include "core/graphic_resource_factory.h" #include "metafile/nml_object.h" #include "log/log.h" using namespace GS; using namespace GS::Core; //------------------------------------------------------------------------------ bool GeometryBIH::FastPolyTest(uint ip, Vector4 &s, Vector4 &d, float t_max) { Polygon &pol = geometry->pol[ip]; Vector4 &pn = geometry->pol_normal[ip]; float dnv = d.Dot(pn); if (dnv >= 0.f) return false; // backface float t = (geometry->vtx[pol.binding[0]].Dot(pn) - s.Dot(pn)) / dnv; if ((t < 0) || ((t_max > 0) && (t > t_max))) return false; // Behind origin or beyond ray length. // Make sure point is in polygon. GeometryBIHAccel &ca = acc[ip]; // Note that the intersection point if offset toward the polygon origin. float pu = s[ca.cu] + d[ca.cu] * t - geometry->vtx[pol.binding[0]][ca.cu]; float pv = s[ca.cv] + d[ca.cv] * t - geometry->vtx[pol.binding[0]][ca.cv]; const float ray_epsilon = -0.000001f; float *k = ca.k; for (int m = 1; m < (pol.vtx_count - 1); ++m) { float u = pv * k[0] + pu * k[1], v, w; if (u < ray_epsilon) goto next; v = pu * k[2] + pv * k[3]; if (v < ray_epsilon) goto next; w = 1 - u - v; if (w < ray_epsilon) goto next; return true; next:; k += 4; } return false; } void GeometryBIH::TraceLeaf(BIH::Node *leaf, float tmin, float tmax, BIH::Trace &, void *parm) { GeometryTrace *trace = (GeometryTrace *)parm; uint *leaf_indice = (uint *)leaf->p; Vector4 *vtx = geometry->vtx; // Test each polygon in leaf. for (uint n = 0; n < leaf->count; ++n) { uint idx = leaf_indice[n]; Vector4 &pn = geometry->pol_normal[idx]; Polygon &pol = geometry->pol[idx]; Material *material = material_table[pol.material].material; trace->tri_test += pol.vtx_count - 2; // Reject back facing polygons. bool backface = false; float dnv = trace->d.Dot(pn); if (dnv >= 0.f) { if (material->renderword & Material::Render_DoubleSided) backface = true; else continue; } float t = (vtx[pol.binding[0]].Dot(pn) - trace->s.Dot(pn)) / dnv; // Reject intersections further away than the current best one. if (trace->has_i && (t >= trace->i_t)) continue; // Plane is outside ray boundaries. if ((t < tmin) || (t > tmax)) continue; // Make sure point is in polygon. GeometryBIHAccel &ca = acc[idx]; // Note that the intersection point if offset toward the polygon origin. float pu = trace->s[ca.cu] + trace->d[ca.cu] * t - vtx[pol.binding[0]][ca.cu], pv = trace->s[ca.cv] + trace->d[ca.cv] * t - vtx[pol.binding[0]][ca.cv]; #define RAY_EPSILON -0.000001f float *k = ca.k; for (int m = 1; m < (pol.vtx_count - 1); ++m) { float u = pv * k[0] + pu * k[1], v, w; if (u < RAY_EPSILON) goto next; v = pu * k[2] + pv * k[3]; if (v < RAY_EPSILON) goto next; w = 1 - u - v; if (w < RAY_EPSILON) goto next; trace->has_i = true; trace->i_t = t; trace->ip = idx; trace->it = m - 1; trace->bi = pol_index[idx]; trace->u = u; trace->v = v; trace->w = w; trace->g = geometry; trace->m = material; trace->st = material_table[pol.material].shader_tree; trace->backface = backface; break; // No need to look further. next:; k += 4; } } } void GeometryBIH::RaytraceGeometry(GeometryTrace &trace, const Vector4 &s, const Vector4 &d, float l) { trace.ip = -1; trace.tri_test = 0; trace.has_i = false; trace.i_t = -1; trace.s = s; trace.d = d; BIH::Trace bih_trace; Raytrace(bih_trace, s, d, l, (void *)&trace); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool GeometryBIH::BuildFromGeometry(ResourceFactory &gf, Geometry *g) { Free(); // Build geometry LUTs. geometry = g; geometry->ComputePolygonIndex(pol_index); // Load geometry resources. material_table.Allocate(geometry->material_table.GetCount()); for (uint n = 0; n < material_table.GetCount(); ++n) { sMaterial m(gf.LoadMaterial(geometry->material_table[n].name)); if (m.IsNull()) return false; if (!m->shader.IsEmpty()) { sShaderTree tree(new ShaderTree); if (NML::LoadFromFile(*tree, m->shader)) material_table[n].shader_tree = tree; } material_table[n].material = m; } // Build volume array to build tree. Array varray(geometry->pol.GetCount()); if (!varray) __ERR__(__LOG_E__ << "Failed to allocate volume objects.\n", false); if (!acc.Allocate(geometry->pol.GetCount())) __ERR__(__LOG_E__ << "Failed to allocate point in polygon acceleration array.\n", false); geometry->ComputePolygonNormal(); for (uint n = 0; n < geometry->pol.GetCount(); ++n) { Polygon &pol = geometry->pol[n]; if (pol.vtx_count < 3) continue; Vector4 *vtx = geometry->vtx; Vector4 u_edge = vtx[pol.binding[1]] - vtx[pol.binding[0]], v_edge = vtx[pol.binding[2]] - vtx[pol.binding[0]]; geometry->pol_normal[n] = u_edge.Cross(v_edge).Normalized(); // Compute plane determinant. acc[n].d = -vtx[pol.binding[0]].Dot(geometry->pol_normal[n]); // Determine most significant axis. Vector4 m = (vtx[pol.binding[1]] - vtx[pol.binding[0]]).Cross(vtx[pol.binding[2]] - vtx[pol.binding[0]]); float x = Types::Abs(m.x), y = Types::Abs(m.y), z = Types::Abs(m.z); uint axis = 2; if ((x >= y) && (x >= z)) axis = 0; else if ((y >= x) && (y >= z)) axis = 1; char cu = (axis + 1) % 3, cv = (axis + 2) % 3; acc[n].cu = cu; acc[n].cv = cv; // Compute point in triangle fixed coefficients. if (acc[n].k.Allocate((pol.vtx_count - 2) * 4)) { float *pk = acc[n].k.c_ptr(); for (int i = 1; i < (pol.vtx_count - 1); ++i) { Vector4 b = vtx[pol.binding[i + 1]] - vtx[pol.binding[0]], c = vtx[pol.binding[i]] - vtx[pol.binding[0]]; float k = 1.f / (b[cu] * c[cv] - b[cv] * c[cu]); *pk++ = b[cu] * k; *pk++ = -b[cv] * k; *pk++ = c[cv] * k; *pk++ = -c[cu] * k; } // Build volume array. varray[n].mn = varray[n].mx = geometry->vtx[pol.binding[0]]; for (uint m = 1; m < pol.vtx_count; ++m) { varray[n].mn.x = Types::Min(vtx[pol.binding[m]].x, varray[n].mn.x); varray[n].mn.y = Types::Min(vtx[pol.binding[m]].y, varray[n].mn.y); varray[n].mn.z = Types::Min(vtx[pol.binding[m]].z, varray[n].mn.z); varray[n].mx.x = Types::Max(vtx[pol.binding[m]].x, varray[n].mx.x); varray[n].mx.y = Types::Max(vtx[pol.binding[m]].y, varray[n].mx.y); varray[n].mx.z = Types::Max(vtx[pol.binding[m]].z, varray[n].mx.z); } } } return Build(geometry->pol.GetCount(), &varray[0]); } void GeometryBIH::Free() { geometry = NULL; material_table.Free(); acc.Free(); Tree::Free(); } //------------------------------------------------------------------------------