257 lines
6.9 KiB
C++
257 lines
6.9 KiB
C++
/* -----------------------------------------------------------------------------
|
|
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 <MinMax> 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();
|
|
}
|
|
//------------------------------------------------------------------------------
|