Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions mplot/NavMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ namespace mplot
}

// Compute the triangle normal for the ordered triplet of triangle vertices, tverts
sm::vec<float, 3> triangle_normal (const sm::vec<sm::vec<float>, 3>& tverts)
sm::vec<float, 3> triangle_normal (const sm::vec<sm::vec<float>, 3>& tverts) const
{
sm::vec<float> n = (tverts[1] - tverts[0]).cross (tverts[2] - tverts[0]);
n.renormalize();
Expand Down Expand Up @@ -317,10 +317,14 @@ namespace mplot
* model. The length of vdir is used to avoid finding the intersection at the 'back' of the
* model.
*
* \param ti_ml The most likely triangle, if you know what it probably is, to reduce the
* search time.
*
* \return a tuple containing crossing location, triangle identity (three indices) and triangle normal vector
*/
std::tuple<sm::vec<float>, std::array<uint32_t, 4>, sm::vec<float>>
find_triangle_crossing (const sm::vec<float>& coord_mf, const sm::vec<float>& vdir) const
find_triangle_crossing (const sm::vec<float>& coord_mf, const sm::vec<float>& vdir,
const std::array<uint32_t, 4> ti_ml = {std::numeric_limits<uint32_t>::max()}) const
{
constexpr auto umax = std::numeric_limits<uint32_t>::max();
constexpr auto fmax = std::numeric_limits<float>::max();
Expand All @@ -333,6 +337,30 @@ namespace mplot

auto isect_d = std::numeric_limits<float>::max(); // distance to intersect

const auto vdsos = vdir.sos();

// Have we been passed a 'most likely triangle' to test first? If so, test it.
if (ti_ml[0] != std::numeric_limits<uint32_t>::max()) {
sm::vec<float> v0 = this->vertex[ti_ml[0]];
sm::vec<float> v1 = this->vertex[ti_ml[1]];
sm::vec<float> v2 = this->vertex[ti_ml[2]];
auto [isect, p] = sm::geometry::ray_tri_intersection<float, float, true, false> (v0, v1, v2, vstart, vdir);
if (isect) {
float d = (p - vstart).sos();
if (d < vdsos) {
sm::vec<sm::vec<float>, 3> tverts = { v0, v1, v2 };
isect_p = p;
isect_ti = ti_ml;
isect_tn = this->triangle_normal (tverts); // compute tn
isect_d = d;
}
}
}
if (isect_d != std::numeric_limits<float>::max()) {
// we found it already!
return { isect_p, isect_ti, isect_tn };
}

for (auto tri : this->triangles) {
auto [ti, tn, tnc, tnd] = tri;
auto [isect, p] = sm::geometry::ray_tri_intersection<float, float, true, false> (this->vertex[ti[0]], this->vertex[ti[1]], this->vertex[ti[2]], vstart, vdir);
Expand All @@ -341,7 +369,7 @@ namespace mplot
// closest one that isn't.
if (isect) {
float d = (p - vstart).sos();
if (d < isect_d && d < vdir.sos()) {
if (d < isect_d && d < vdsos) {
isect_p = p;
isect_ti = ti;
isect_tn = tn;
Expand All @@ -350,8 +378,9 @@ namespace mplot
}
}

if (isect_p[0] == fmax) {
// Found no triangle intersection; check vertices, in case vdir points perfectly at a vertex
if (isect_p[0] == fmax && this->vertex.size() < 10000) {
// Found no triangle intersection; check vertices, in case vdir points perfectly at a vertex.
// This can be computationally expensive, hence the hacky check, above.
for (uint32_t ti = 0; ti < this->vertex.size(); ++ti) {
sm::vec<float> vertex_n = this->find_vertex_normal (ti); // also loops
vertex_n.renormalize();
Expand Down Expand Up @@ -805,17 +834,22 @@ namespace mplot
* large, flat, one-sided landscape, we want to make vdir long. search_dist_mult is applied
* to vdir.
*
* \param ti_ml The most likely triangle, if you know what it probably is, to reduce the
* search time.
*
* \return tuple containing: the hit point in scene coordinates; the triangle normal of the
* triangle we hit; and the indices of the triangle we hit.
*/
std::tuple<sm::vec<float>, sm::vec<float>, std::array<uint32_t, 4>>
find_triangle_hit (const sm::mat44<float>& model_to_scene,
const sm::vec<float>& camloc_mf, const sm::vec<float>& vdir)
const sm::vec<float>& camloc_mf, const sm::vec<float>& vdir,
const std::array<uint32_t, 4> ti_ml = {std::numeric_limits<uint32_t>::max()})
{
this->ti0 = {};
this->tn0 = {};
sm::vec<float> hit = {};
std::tie (hit, this->ti0, this->tn0) = this->find_triangle_crossing (camloc_mf, vdir);
// Want to pass 'best tri' to this
std::tie (hit, this->ti0, this->tn0) = this->find_triangle_crossing (camloc_mf, vdir, ti_ml);

if (this->ti0[0] == std::numeric_limits<uint32_t>::max()) { std::cout << __func__ << ": No hit\n"; }

Expand Down
52 changes: 39 additions & 13 deletions mplot/compoundray/EyeVisual.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ namespace mplot::compoundray
if (show_3d) {
// Replace colours for the 3D part of the model
int num_vertices = disc_vertices;
if (this->show_cones == true) {
if (this->show_cones == true && this->cones_will_show) {
num_vertices = cone_vertices + disc_vertices;
} // else num_vertices = disc_vertices;

Expand Down Expand Up @@ -280,19 +280,21 @@ namespace mplot::compoundray
float ray_radius = ommrng.span().max() / 500.0f;

// Find mean minimum ommatidial distance
sm::vvec<float> dist_to_other (n_omm, 0.0f);
sm::vvec<float> min_dist_to_other (n_omm, 0.0f);
for (size_t i = 0u; i < n_omm; ++i) {
for (size_t j = 0u; j < n_omm; ++j) {
if (i == j) {
dist_to_other[j] = 10000.0f;
} else {
dist_to_other[j] = ((*ommatidia)[i].relativePosition - (*ommatidia)[j].relativePosition).length();
if (this->min_dist_to_other.empty()) {
sm::vvec<float> dist_to_other (n_omm, 0.0f);
min_dist_to_other.resize (n_omm, 0.0f);
for (size_t i = 0u; i < n_omm; ++i) {
for (size_t j = 0u; j < n_omm; ++j) {
if (i == j) {
dist_to_other[j] = 10000.0f;
} else {
dist_to_other[j] = ((*ommatidia)[i].relativePosition - (*ommatidia)[j].relativePosition).length();
}
}
min_dist_to_other[i] = dist_to_other.min();
}
min_dist_to_other[i] = dist_to_other.min();
}
std::cerr << "Mean ommatidial distance: " << min_dist_to_other.mean() << std::endl;
std::cerr << "Mean ommatidial distance: " << this->min_dist_to_other.mean() << std::endl;

// First find out if all focal points are 0
this->focal_point_sum = 0.0f;
Expand All @@ -301,6 +303,7 @@ namespace mplot::compoundray
}

if (show_3d && this->focal_point_sum > 0.0f) {
std::cout << "Stanza 1\n";
// We have focal points, so draw with the relativePosition representing the centre
// of the ommatidial lens - the base of a cone - which then extends back to the cone
// tip, which can be thought of as the location of the ommatidial 'sensor'
Expand All @@ -316,22 +319,32 @@ namespace mplot::compoundray
sm::vec<float, 3> ommatidial_detector_point = pos - dir * focal_point;
// work out a radius from acceptance angle and focal_point
// The discs are based on the inter-ommatidial distances in space, which have to have been computed
float dw = min_dist_to_other[i];
float dw = this->min_dist_to_other[i];
this->computeTube (pos, pos + (0.05f * dw * dir), colour, colour, dw * 0.5f, tube_faces);

// This visualizes the optical cones
if (this->show_cones == true && this->show_fov == false) {
float optical_radius = focal_point * std::tan (angle / 2.0f);
// Colour comes from ommData. ringoffset is 1.0f
this->computeCone (pos, ommatidial_detector_point, 0.0f, colour, optical_radius, tube_faces);
this->cones_will_show = true;

} else if (this->show_fov == true) {
// do a cone of angle 'acceptanceAngle' using user-supplied cone_length, starting FROM the disc to show field of view of the eye
sm::vec<float, 3> ommatidial_cone_pos = pos + dir * this->cone_length;
float radius = this->cone_length * std::tan (angle / 2.0f);
this->computeCone (ommatidial_cone_pos, pos, 0.0f, colour, radius, tube_faces);
}
}

if ((this->show_cones == true || this->show_fov == true) && n_omm > 0) {
this->cones_will_show = true;
} else {
this->cones_will_show = false;
}

} else if (show_3d && this->focal_point_sum <= 0.0f) {
std::cout << "Stanza 2\n";
// All our focal_points are 0. Don't have focal point offset to help define our
// cones, only acceptance angle. Use manually specified tube_length (or computed
// radius) to figure out the size of a cone, whose tip is the location of the
Expand All @@ -344,7 +357,7 @@ namespace mplot::compoundray
sm::vec<float, 3> dir = (*ommatidia)[i].relativeDirection;
dir.renormalize();

float dw = min_dist_to_other[i];
float dw = this->min_dist_to_other[i];
this->computeTube (pos, pos + (0.05f * dw * dir), colour, colour, dw * 0.5f, tube_faces);
// We don't have a focal length to show cones, but we can still show the acceptance angle
if (this->show_fov == true) {
Expand All @@ -354,6 +367,15 @@ namespace mplot::compoundray
this->computeCone (ommatidial_cone_pos, pos, 0.0f, colour, radius, tube_faces);
}
}

if (this->show_fov == true && n_omm > 0) {
this->cones_will_show = true;
} else {
this->cones_will_show = false;
}

} else {
this->cones_will_show = false;
}

// 2D projections
Expand Down Expand Up @@ -525,10 +547,14 @@ namespace mplot::compoundray
// If true, show 'field of view' cones. Overrides show_cones. Control size of cones with
// this->cone_length
bool show_fov = false;
// either show_cones or show_fov are enabled and cones have been drawn
bool cones_will_show = false;
// The colours detected by each ommatidium
std::vector<std::array<float, 3>>* ommData = nullptr;
// The position and orientation of each ommatidium
std::vector<mplot::compoundray::Ommatidium>* ommatidia = nullptr;
// Distances to the nearest ommatidia, for choosing disc size. Computed once only
sm::vvec<float> min_dist_to_other = {};
// An optional head mesh
const mplot::meshgroup* head_mesh = nullptr;
// If sum is 0, then we have a special case for rendering the eye as we have no focal point
Expand Down