finish drag selection
This commit is contained in:
@@ -58,14 +58,14 @@ namespace math
|
||||
std::array<glm::vec3, 8> box_points(glm::vec3 const& box_min, glm::vec3 const& box_max)
|
||||
{
|
||||
return std::array<glm::vec3, 8> {
|
||||
glm::vec3(box_max.x, box_max.y, box_max.z),
|
||||
glm::vec3(box_max.x, box_max.y, box_min.z),
|
||||
glm::vec3(box_max.x, box_min.y, box_max.z),
|
||||
glm::vec3(box_max.x, box_min.y, box_min.z),
|
||||
glm::vec3(box_min.x, box_max.y, box_max.z),
|
||||
glm::vec3(box_min.x, box_max.y, box_min.z),
|
||||
glm::vec3(box_min.x, box_min.y, box_max.z),
|
||||
glm::vec3(box_min.x, box_min.y, box_min.z)
|
||||
glm::vec3(box_max.x, box_max.y, box_max.z), // 0: Top-right-front
|
||||
glm::vec3(box_max.x, box_max.y, box_min.z), // 1: Top-right-back
|
||||
glm::vec3(box_max.x, box_min.y, box_max.z), // 2: Bottom-right-front
|
||||
glm::vec3(box_max.x, box_min.y, box_min.z), // 3: Bottom-right-back
|
||||
glm::vec3(box_min.x, box_max.y, box_max.z), // 4: Top-left-front
|
||||
glm::vec3(box_min.x, box_max.y, box_min.z), // 5: Top-left-back
|
||||
glm::vec3(box_min.x, box_min.y, box_max.z), // 6: Bottom-left-front
|
||||
glm::vec3(box_min.x, box_min.y, box_min.z) // 7: Bottom-left-back
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace math
|
||||
aabb(std::vector<glm::vec3> const& points);
|
||||
|
||||
std::array<glm::vec3, 8> all_corners() const;
|
||||
inline glm::vec3 center() const { return (min + max) * 0.5f; };
|
||||
|
||||
glm::vec3 min;
|
||||
glm::vec3 max;
|
||||
|
||||
@@ -8,37 +8,23 @@
|
||||
namespace math
|
||||
{
|
||||
std::optional<float> ray::intersect_bounds
|
||||
(glm::vec3 const& min, glm::vec3 const& max) const
|
||||
(glm::vec3 const& min, glm::vec3 const& max) const noexcept
|
||||
{
|
||||
float tmin (std::numeric_limits<float>::lowest());
|
||||
float tmax (std::numeric_limits<float>::max());
|
||||
|
||||
if (_direction.x != 0.0f)
|
||||
{
|
||||
float const tx1 ((min.x - _origin.x) / _direction.x);
|
||||
float const tx2 ((max.x - _origin.x) / _direction.x);
|
||||
auto calculate_tmin_tmax = [](float origin, float direction, float min, float max, float& tmin, float& tmax) {
|
||||
if (direction != 0.0f) {
|
||||
float t1 = (min - origin) / direction;
|
||||
float t2 = (max - origin) / direction;
|
||||
tmin = std::max(tmin, std::min(t1, t2));
|
||||
tmax = std::min(tmax, std::max(t1, t2));
|
||||
}
|
||||
};
|
||||
|
||||
tmin = std::max (tmin, std::min (tx1, tx2));
|
||||
tmax = std::min (tmax, std::max (tx1, tx2));
|
||||
}
|
||||
|
||||
if (_direction.y != 0.0f)
|
||||
{
|
||||
float const ty1 ((min.y - _origin.y) / _direction.y);
|
||||
float const ty2 ((max.y - _origin.y) / _direction.y);
|
||||
|
||||
tmin = std::max (tmin, std::min (ty1, ty2));
|
||||
tmax = std::min (tmax, std::max (ty1, ty2));
|
||||
}
|
||||
|
||||
if (_direction.z != 0.0f)
|
||||
{
|
||||
float const tz1 ((min.z - _origin.z) / _direction.z);
|
||||
float const tz2 ((max.z - _origin.z) / _direction.z);
|
||||
|
||||
tmin = std::max (tmin, std::min (tz1, tz2));
|
||||
tmax = std::min (tmax, std::max (tz1, tz2));
|
||||
}
|
||||
calculate_tmin_tmax(_origin.x, _direction.x, min.x, max.x, tmin, tmax);
|
||||
calculate_tmin_tmax(_origin.y, _direction.y, min.y, max.y, tmin, tmax);
|
||||
calculate_tmin_tmax(_origin.z, _direction.z, min.z, max.z, tmin, tmax);
|
||||
|
||||
if (tmax >= tmin)
|
||||
{
|
||||
@@ -49,7 +35,7 @@ namespace math
|
||||
}
|
||||
|
||||
std::optional<float> ray::intersect_triangle
|
||||
(glm::vec3 const& v0, glm::vec3 const& v1, glm::vec3 const& v2) const
|
||||
(glm::vec3 const& v0, glm::vec3 const& v1, glm::vec3 const& v2) const noexcept
|
||||
{
|
||||
glm::vec3 e1 (v1 - v0);
|
||||
glm::vec3 e2 (v2 - v0);
|
||||
@@ -58,7 +44,8 @@ namespace math
|
||||
|
||||
float const det = glm::dot(e1, P);
|
||||
|
||||
if (det == 0.0f)
|
||||
constexpr float epsilon = std::numeric_limits<float>::epsilon();
|
||||
if (std::fabs(det) < epsilon) // if (det == 0.0f)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -81,7 +68,7 @@ namespace math
|
||||
|
||||
float const dott = glm::dot(e2 , Q) / det;
|
||||
|
||||
if (dott > std::numeric_limits<float>::min())
|
||||
if (dott > epsilon) // if (dott > std::numeric_limits<float>::min())
|
||||
{
|
||||
return dott;
|
||||
}
|
||||
|
||||
@@ -2,25 +2,32 @@
|
||||
#pragma once
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <optional>
|
||||
#include <cmath>
|
||||
|
||||
namespace math
|
||||
{
|
||||
struct ray
|
||||
{
|
||||
ray (glm::vec3 origin, glm::vec3 const& direction): _origin (std::move (origin)), _direction (glm::normalize(direction))
|
||||
{}
|
||||
ray (glm::vec3 origin, glm::vec3 const& direction)
|
||||
: _origin (std::move (origin)), _direction (glm::normalize(direction))
|
||||
{
|
||||
if (std::isnan(_direction.x) || std::isnan(_direction.y) || std::isnan(_direction.z))
|
||||
{
|
||||
std::cout << "Vector contains NaN values!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
ray (glm::mat4x4 const& transform, ray const& other): ray (
|
||||
glm::vec3(
|
||||
(transform * glm::vec4(other._origin.x, other._origin.y, other._origin.z, 1.0))),
|
||||
glm::vec3((transform * glm::vec4(other._direction.x, other._direction.y, other._direction.z, 0.0)))
|
||||
ray (glm::mat4x4 const& transform, ray const& other)
|
||||
: ray (
|
||||
glm::vec3(transform * glm::vec4(other._origin, 1.0f)),
|
||||
glm::vec3((glm::mat3(transform) * other._direction))
|
||||
)
|
||||
{}
|
||||
|
||||
std::optional<float> intersect_bounds
|
||||
(glm::vec3 const& _min, glm::vec3 const& _max) const;
|
||||
(glm::vec3 const& _min, glm::vec3 const& _max) const noexcept;
|
||||
std::optional<float> intersect_triangle
|
||||
(glm::vec3 const& _v0, glm::vec3 const& _v1, glm::vec3 const& _v2) const;
|
||||
(glm::vec3 const& _v0, glm::vec3 const& _v1, glm::vec3 const& _v2) const noexcept;
|
||||
|
||||
glm::vec3 position (float distance) const
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace misc
|
||||
// Perspective division to move to normalized device coordinates (NDC)
|
||||
float ndcX = clipSpacePos.x / clipSpacePos.w;
|
||||
float ndcY = clipSpacePos.y / clipSpacePos.w;
|
||||
float ndcZ = clipSpacePos.z / clipSpacePos.w;
|
||||
// float ndcZ = clipSpacePos.z / clipSpacePos.w; // unnecessary but could be used for extra checks
|
||||
|
||||
// If the point is out of the normalized device coordinates range, it's off-screen
|
||||
if (ndcX < -1.0f || ndcX > 1.0f || ndcY < -1.0f || ndcY > 1.0f)
|
||||
@@ -55,6 +55,8 @@ namespace misc
|
||||
// Convert NDC to screen space coordinates
|
||||
clipSpacePos.x = (ndcX + 1.0f) * 0.5f * viewport_width;
|
||||
clipSpacePos.y = (1.0f - (ndcY + 1.0f) * 0.5f) * viewport_height;
|
||||
// test inverting the MapView::normalized_device_coords formula.
|
||||
// clipSpacePos.y = (1.0f - ndcY) * 0.5f * viewport_height;
|
||||
|
||||
// from MapView::normalized_device_coords
|
||||
// x 2.0f * x / viewport_width - 1.0f
|
||||
|
||||
@@ -68,6 +68,12 @@ namespace misc
|
||||
|
||||
bool pointInside(glm::vec3 point, std::array<glm::vec3, 2> const& extents);
|
||||
bool pointInside(glm::vec2 point, std::array<glm::vec2, 2> const& extents);
|
||||
|
||||
inline glm::vec4 normalized_device_coords(int x, int y, int screen_width, int screen_height)
|
||||
{
|
||||
return { 2.0f * x / screen_width - 1.0f, 1.0f - 2.0f * y / screen_height, 0.0f, 1.0f };
|
||||
}
|
||||
|
||||
void minmax(glm::vec3* a, glm::vec3* b);
|
||||
|
||||
inline int rounded_int_div(int value, int div)
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
|
||||
|
||||
#include <noggit/ModelInstance.h>
|
||||
#include <noggit/Log.h>
|
||||
#include <noggit/Misc.h> // checkinside
|
||||
#include <noggit/Model.h> // Model, etc.
|
||||
#include <noggit/WMOInstance.h>
|
||||
#include <noggit/ContextObject.hpp>
|
||||
#include <noggit/rendering/Primitives.hpp>
|
||||
#include <opengl/scoped.hpp>
|
||||
#include <opengl/shader.hpp>
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
#include <math/bounding_box.hpp>
|
||||
#include <math/frustum.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <noggit/Log.h>
|
||||
#include <noggit/Misc.h> // checkinside
|
||||
#include <noggit/Model.h> // Model, etc.
|
||||
#include <noggit/ModelInstance.h>
|
||||
#include <noggit/WMOInstance.h>
|
||||
#include <noggit/ContextObject.hpp>
|
||||
#include <noggit/rendering/Primitives.hpp>
|
||||
#include <opengl/scoped.hpp>
|
||||
#include <opengl/shader.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -88,12 +90,17 @@ void ModelInstance::draw_box (glm::mat4x4 const& model_view
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<int, int, int>> ModelInstance::intersect (glm::mat4x4 const& model_view
|
||||
void ModelInstance::intersect (glm::mat4x4 const& model_view
|
||||
, math::ray const& ray
|
||||
, selection_result* results
|
||||
, int animtime
|
||||
)
|
||||
{
|
||||
if (!finishedLoading() || model->loading_failed())
|
||||
return;
|
||||
|
||||
ensureExtents();
|
||||
|
||||
std::vector<std::tuple<int, int, int>> triangle_indices;
|
||||
math::ray subray (_transform_mat_inverted, ray);
|
||||
|
||||
@@ -102,7 +109,7 @@ std::vector<std::tuple<int, int, int>> ModelInstance::intersect (glm::mat4x4 con
|
||||
)
|
||||
)
|
||||
{
|
||||
return triangle_indices;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto&& result : model->intersect (model_view, subray, animtime))
|
||||
@@ -112,7 +119,7 @@ std::vector<std::tuple<int, int, int>> ModelInstance::intersect (glm::mat4x4 con
|
||||
results->emplace_back (result.first * scale, this);
|
||||
triangle_indices.emplace_back(result.second);
|
||||
}
|
||||
return triangle_indices;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -189,23 +196,26 @@ void ModelInstance::recalcExtents()
|
||||
//! the model is bad itself. We *could* detect that case and explicitly
|
||||
//! assume {-1, 1} then, to be nice to fuckported models.
|
||||
|
||||
auto corners_in_world = std::vector<glm::vec3>();
|
||||
std::array<glm::vec3, 8> corners_in_world;
|
||||
auto transform = misc::transform_model_box_coords;
|
||||
auto points = relative_to_model.all_corners();
|
||||
for (auto& point : points)
|
||||
std::array<glm::vec3, 8> points = relative_to_model.all_corners();
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
// for (auto& point : points)
|
||||
{
|
||||
point = transform(point);
|
||||
corners_in_world.push_back(point);
|
||||
// points[i] = transform(points[i]);
|
||||
corners_in_world[i] = transform(points[i]);
|
||||
}
|
||||
|
||||
auto rotated_corners_in_world = std::vector<glm::vec3>();
|
||||
auto transposedMat = _transform_mat;
|
||||
for (auto const& point : corners_in_world)
|
||||
std::array<glm::vec3, 8> rotated_corners_in_world;
|
||||
// for (auto const& point : corners_in_world)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
rotated_corners_in_world.emplace_back(transposedMat * glm::vec4(point, 1.f));
|
||||
rotated_corners_in_world[i] = _transform_mat * glm::vec4(corners_in_world[i], 1.f);
|
||||
}
|
||||
|
||||
math::aabb const bounding_of_rotated_points (rotated_corners_in_world);
|
||||
math::aabb const bounding_of_rotated_points (std::vector<glm::vec3>(rotated_corners_in_world.begin()
|
||||
, rotated_corners_in_world.end()));
|
||||
|
||||
extents[0] = bounding_of_rotated_points.min;
|
||||
extents[1] = bounding_of_rotated_points.max;
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
);
|
||||
|
||||
|
||||
std::vector<std::tuple<int, int, int>> intersect(glm::mat4x4 const& model_view
|
||||
void intersect(glm::mat4x4 const& model_view
|
||||
, math::ray const&
|
||||
, selection_result*
|
||||
, int animtime
|
||||
|
||||
@@ -136,6 +136,9 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
|
||||
|
||||
void WMOInstance::intersect (math::ray const& ray, selection_result* results, bool do_exterior)
|
||||
{
|
||||
if (!finishedLoading() || wmo->loading_failed())
|
||||
return;
|
||||
|
||||
ensureExtents();
|
||||
|
||||
if (!ray.intersect_bounds (extents[0], extents[1]))
|
||||
@@ -160,7 +163,7 @@ std::array<glm::vec3, 2> const& WMOInstance::getExtents()
|
||||
|
||||
void WMOInstance::ensureExtents()
|
||||
{
|
||||
if (_need_recalc_extents && wmo->finishedLoading())
|
||||
if ( (_need_recalc_extents || _update_group_extents) && wmo->finishedLoading())
|
||||
{
|
||||
recalcExtents();
|
||||
}
|
||||
@@ -234,11 +237,11 @@ void WMOInstance::recalcExtents()
|
||||
glm::vec3 wmo_max(misc::transform_model_box_coords(wmo->extents[1]));
|
||||
|
||||
auto&& root_points = math::aabb(wmo_min, wmo_max).all_corners();
|
||||
auto adjustedPoints = std::vector<glm::vec3>();
|
||||
std::array<glm::vec3, 8> adjustedPoints;
|
||||
|
||||
for (auto const& point : root_points)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
adjustedPoints.push_back(_transform_mat * glm::vec4(point, 1.f));
|
||||
adjustedPoints[i] = _transform_mat * glm::vec4(root_points[i], 1.f);
|
||||
}
|
||||
|
||||
points.insert(points.end(), adjustedPoints.begin(), adjustedPoints.end());
|
||||
@@ -248,24 +251,24 @@ void WMOInstance::recalcExtents()
|
||||
auto const& group = wmo->groups[i];
|
||||
|
||||
auto&& group_points = math::aabb(group.BoundingBoxMin, group.BoundingBoxMax).all_corners();
|
||||
auto adjustedGroupPoints = std::vector<glm::vec3>();
|
||||
std::array<glm::vec3, 8> adjustedGroupPoints;
|
||||
|
||||
for (auto const& point : group_points)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
adjustedGroupPoints.push_back(_transform_mat * glm::vec4(point, 1.f));
|
||||
adjustedGroupPoints[i] = _transform_mat * glm::vec4(group_points[i], 1.f);
|
||||
}
|
||||
|
||||
|
||||
points.insert(points.end(), adjustedGroupPoints.begin(), adjustedGroupPoints.end());
|
||||
|
||||
if (group.has_skybox() /* || _update_group_extents*/)
|
||||
if (group.has_skybox() || _update_group_extents)
|
||||
{
|
||||
math::aabb const group_aabb(adjustedGroupPoints);
|
||||
math::aabb const group_aabb(std::vector<glm::vec3>(adjustedGroupPoints.begin()
|
||||
, adjustedGroupPoints.end()));
|
||||
|
||||
group_extents[i] = {group_aabb.min, group_aabb.max};
|
||||
_update_group_extents = false;
|
||||
}
|
||||
}
|
||||
_update_group_extents = false;
|
||||
|
||||
math::aabb const wmo_aabb(points);
|
||||
|
||||
|
||||
@@ -1767,7 +1767,7 @@ void World::loadAllTiles()
|
||||
|
||||
if (mTile)
|
||||
{
|
||||
mTile->wait_until_loaded();
|
||||
// mTile->wait_until_loaded();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3626,13 +3626,14 @@ void World::select_objects_in_area(
|
||||
}
|
||||
|
||||
glm::mat4 VPmatrix = projection * view;
|
||||
glm::mat4x4 const invertedViewMatrix = glm::inverse(VPmatrix);
|
||||
|
||||
constexpr int max_position_raycast_processing = 10000;
|
||||
constexpr int max_bounds_raycast_processing = 5000; // when selecting large amount of objects, avoid doing complex ray calculations to not freeze
|
||||
constexpr float bounds_check_scale = 0.8f; // size of the bounding box to use when interesecting withs election rectangle
|
||||
constexpr float obj_raycast_min_size = 30.0f; // screen size rectangle lenght in pixels
|
||||
|
||||
int processed_obj_count = 0;
|
||||
int processed_obj_count = 0; // num objects that had a raycast test at least once
|
||||
// int debug_count_obj_min_size = 0;
|
||||
// int debug_count_obj_min_size_not = 0;
|
||||
|
||||
@@ -3674,105 +3675,161 @@ void World::select_objects_in_area(
|
||||
for (auto& pair : tile->getObjectInstances())
|
||||
{
|
||||
[[unlikely]]
|
||||
if (!pair.first->finishedLoading())
|
||||
if (!pair.first->finishedLoading() || pair.first->loading_failed())
|
||||
continue;
|
||||
|
||||
auto objectType = pair.second[0]->which();
|
||||
|
||||
[[unlikely]]
|
||||
if (!(objectType == eMODEL || objectType == eWMO))
|
||||
if (pair.second.empty())
|
||||
continue;
|
||||
|
||||
SceneObjectTypes objectType = pair.second[0]->which();
|
||||
|
||||
// check if object is hidden
|
||||
if (objectType == eWMO)
|
||||
{
|
||||
WMOInstance* model_instance = static_cast<WMOInstance*>(pair.second[0]);
|
||||
|
||||
if (model_instance->wmo->is_hidden())
|
||||
continue;
|
||||
}
|
||||
else if (objectType == eMODEL)
|
||||
{
|
||||
ModelInstance* model_instance = static_cast<ModelInstance*>(pair.second[0]);
|
||||
|
||||
if (model_instance->model->is_hidden())
|
||||
continue;
|
||||
}
|
||||
else
|
||||
[[unlikely]]
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& instance : pair.second)
|
||||
{
|
||||
// problem : M2s have additional sized based culling with >isInRenderDist()
|
||||
// if (!instance->_rendered_last_frame)
|
||||
// continue;
|
||||
|
||||
bool do_selection = false;
|
||||
const float distance = glm::distance(camera_position, instance->pos);
|
||||
if (distance > user_depth || distance > renderer()->cullDistance())
|
||||
continue;
|
||||
|
||||
// Old code to check position point instead of bound box
|
||||
math::aabb obj_aabb(instance->getExtents()[0], instance->getExtents()[1]);
|
||||
auto aabb_center = obj_aabb.center();
|
||||
|
||||
bool point_valid = false;
|
||||
auto origin_screen_pos = misc::projectPointToScreen(aabb_center, VPmatrix, viewport_width, viewport_height, point_valid);
|
||||
// if screenPos.w < 0.0f, object is behind camera
|
||||
// check object bounding radius instead to compare the object's size, if it clips with the camera.
|
||||
if (!origin_screen_pos.w < -instance->getBoundingRadius())
|
||||
{
|
||||
glm::vec4 screenPos = VPmatrix * glm::vec4(instance->pos, 1.0f);
|
||||
|
||||
// if screenPos.w < 0.0f, object is behind camera
|
||||
// check object bounding radius instead to compare the object's size, if it clips with the camera.
|
||||
if (screenPos.w < -instance->getBoundingRadius())
|
||||
continue;
|
||||
|
||||
screenPos.x /= screenPos.w;
|
||||
screenPos.y /= screenPos.w;
|
||||
|
||||
// Convert normalized device coordinates (NDC) to screen coordinates
|
||||
screenPos.x = (screenPos.x + 1.0f) * 0.5f * viewport_width;
|
||||
screenPos.y = (1.0f - (screenPos.y + 1.0f) * 0.5f) * viewport_height;
|
||||
|
||||
|
||||
float distance = glm::distance(camera_position, instance->pos);
|
||||
if (distance > user_depth)
|
||||
continue;
|
||||
|
||||
// check if position(origin) point is within rectangle first because it is much cheaper
|
||||
{
|
||||
const glm::vec2 screenPos2D = glm::vec2(screenPos);
|
||||
if (misc::pointInside(screenPos2D, selection_box))
|
||||
{
|
||||
processed_obj_count++;
|
||||
|
||||
// check if point is occluded by terrain
|
||||
if (processed_obj_count < max_position_raycast_processing
|
||||
&& !is_point_occluded_by_terrain(instance->pos, view, VPmatrix, viewport_width, viewport_height, camera_position))
|
||||
{
|
||||
do_selection = true;
|
||||
}
|
||||
// else
|
||||
// bool debug_breakpoint = true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's not, check again if bounding box is within selection
|
||||
// we check _rendered_last_frame because m2s that are too small on screen already don't render
|
||||
if (!do_selection && instance->_rendered_last_frame && (processed_obj_count < max_bounds_raycast_processing) )
|
||||
bool do_selection = false;
|
||||
// check if position point is within rectangle first because it is much cheaper
|
||||
{
|
||||
const glm::vec2 screenPos2D = glm::vec2(origin_screen_pos);
|
||||
if (misc::pointInside(screenPos2D, selection_box))
|
||||
{
|
||||
// processed_obj_count++;
|
||||
// check if center point is occluded by terrain
|
||||
if (processed_obj_count < max_position_raycast_processing &&
|
||||
!is_point_occluded_by_terrain(aabb_center, view, VPmatrix, viewport_width, viewport_height, camera_position))
|
||||
{
|
||||
// if not occluded, select it and skip other checks
|
||||
do_selection = true;
|
||||
}
|
||||
// else
|
||||
// bool debug_breakpoint = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if center point raycast didn't succeed, check again if bounding box is within selection in 2D screen space to test other points
|
||||
std::array<glm::vec2, 2> aabb_screnbounds;
|
||||
if (!do_selection)
|
||||
{
|
||||
bool valid = false;
|
||||
auto screenBounds = misc::getAABBScreenBounds(instance->getExtents(), VPmatrix
|
||||
aabb_screnbounds = misc::getAABBScreenBounds(instance->getExtents(), VPmatrix
|
||||
, viewport_width, viewport_height, valid, 0.75f);
|
||||
|
||||
if (valid && math::boxIntersects(screenBounds[0], screenBounds[1]
|
||||
if (valid && math::boxIntersects(aabb_screnbounds[0], aabb_screnbounds[1]
|
||||
, selection_box[0], selection_box[1]))
|
||||
{
|
||||
// do_selection = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if rectangles don't intersect, just skip
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Optimization : Only do raycast bounds checks for object that take enough screen space
|
||||
// if object is too small checking other points is useless
|
||||
if (!do_selection)
|
||||
{
|
||||
if (glm::distance(screenBounds[0], screenBounds[1]) < obj_raycast_min_size)
|
||||
{
|
||||
// debug_count_obj_min_size++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
float bounds_size = glm::distance(aabb_screnbounds[0], aabb_screnbounds[1]);
|
||||
if ( bounds_size < obj_raycast_min_size || !instance->_rendered_last_frame)
|
||||
{
|
||||
// debug_count_obj_min_size_not++;
|
||||
continue;
|
||||
}
|
||||
else if (processed_obj_count > max_bounds_raycast_processing)
|
||||
{
|
||||
// select it anyways
|
||||
do_selection = true;
|
||||
// debug_count_obj_min_size++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Occlusion test on object's corners (that are in selection box)
|
||||
// uses ray casting, very expensive
|
||||
if (!do_selection)
|
||||
// // we check _rendered_last_frame because m2s that are too small on screen already don't render
|
||||
if (!do_selection /* && instance->_rendered_last_frame && (processed_obj_count < max_bounds_raycast_processing)*/)
|
||||
{
|
||||
math::aabb obj_aabb(instance->getExtents()[0], instance->getExtents()[1]);
|
||||
processed_obj_count++;
|
||||
|
||||
// get the center of the intersectino rectangle
|
||||
// 1 : get the intersection rectangle of screen space and bounding box
|
||||
glm::vec2 intersectionMin = glm::max(aabb_screnbounds[0], selection_box[0]);
|
||||
glm::vec2 intersectionMax = glm::min(aabb_screnbounds[1], selection_box[1]);
|
||||
// Check for Valid Intersection:
|
||||
if (intersectionMin.x < intersectionMax.x && intersectionMin.y < intersectionMax.y) {
|
||||
// Valid intersection
|
||||
}
|
||||
else {
|
||||
// No intersection, shouldn't happen
|
||||
continue;
|
||||
}
|
||||
// 2 : get center
|
||||
glm::vec2 intersectionCenter = (intersectionMin + intersectionMax) * 0.5f;
|
||||
// 3 : convert 2D screenspace point back to 3d
|
||||
glm::vec4 normalisedView = invertedViewMatrix * misc::normalized_device_coords(intersectionCenter.x, intersectionCenter.y,
|
||||
viewport_width, viewport_height);
|
||||
glm::vec3 intersectionCenter_pos = glm::vec3(normalisedView.x / normalisedView.w, normalisedView.y / normalisedView.w, normalisedView.z / normalisedView.w);
|
||||
|
||||
|
||||
auto obj_aabb_corners = obj_aabb.all_corners();
|
||||
|
||||
// int required_num_unoccluded_corers = 6; // require 6 of 8 corners to not be occluded
|
||||
// Iterate key points instead of all 8 corners
|
||||
std::vector<glm::vec3> key_points = {
|
||||
// intersectionCenter_pos,
|
||||
// (obj_aabb_corners[0] + obj_aabb_corners[6]) * 0.5f, // Center between top corners
|
||||
obj_aabb_corners[0], // Top-right-front
|
||||
obj_aabb_corners[5], // Top-left-back
|
||||
obj_aabb_corners[4], // Top-left-front
|
||||
obj_aabb_corners[1] // Top-right-back
|
||||
};
|
||||
|
||||
// int required_num_unoccluded_corners = 2;
|
||||
bool object_occluded = true;
|
||||
|
||||
for (const auto& corner : obj_aabb_corners)
|
||||
// check if points are occluded by terrain
|
||||
for (const auto& corner : key_points /*obj_aabb_corners*/)
|
||||
{
|
||||
// TODO : only need to do max top left and max top right in 2d instead of all corners?
|
||||
|
||||
@@ -3789,6 +3846,7 @@ void World::select_objects_in_area(
|
||||
|
||||
if (!corner_occluded)
|
||||
{
|
||||
// if just one point isn't occluded is enough, select object
|
||||
object_occluded = false;
|
||||
break;
|
||||
}
|
||||
@@ -3801,31 +3859,12 @@ void World::select_objects_in_area(
|
||||
if (!do_selection)
|
||||
continue;
|
||||
|
||||
auto& obj = instance;
|
||||
auto which = obj->which();
|
||||
if (which == eWMO)
|
||||
{
|
||||
auto model_instance = static_cast<WMOInstance*>(obj);
|
||||
|
||||
if (!model_instance->wmo->is_hidden())
|
||||
{
|
||||
this->add_to_selection(obj, false, false);
|
||||
}
|
||||
}
|
||||
else if (which == eMODEL)
|
||||
{
|
||||
auto model_instance = static_cast<ModelInstance*>(obj);
|
||||
|
||||
if (!model_instance->model->is_hidden())
|
||||
{
|
||||
this->add_to_selection(obj, false, false);
|
||||
}
|
||||
}
|
||||
add_to_selection(instance, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->update_selection_pivot();
|
||||
this->update_selection_pivot();
|
||||
}
|
||||
|
||||
|
||||
@@ -3868,14 +3907,13 @@ bool World::is_point_occluded_by_terrain(const glm::vec3& point,
|
||||
for (const auto& terrain_hit : terrain_intersect_results)
|
||||
{
|
||||
// if terrain hit is further, skip
|
||||
if (terrain_hit.first + 10.0f > distance) // add some leeway, skip hits that are too close, especially for the terrain at object's origin
|
||||
if (terrain_hit.first + 5.0f > distance) // add some leeway, skip hits that are too close, especially for the terrain at object's origin
|
||||
continue;
|
||||
|
||||
return true;
|
||||
|
||||
/*
|
||||
auto const& hitChunkInfo = std::get<selected_chunk_type>(terrain_hit.second);
|
||||
|
||||
/*
|
||||
// check if terrain hit is higher than the object's corner in 2D screen space
|
||||
bool point_valid = false;
|
||||
auto terrain_hit_screen_pos = misc::projectPointToScreen(hitChunkInfo.position, VPmatrix, viewport_width, viewport_height, point_valid);
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace Noggit::Rendering
|
||||
[[nodiscard]] std::unique_ptr<Skies>& skies() { return _skies; };
|
||||
|
||||
float _view_distance;
|
||||
inline float cullDistance() const { return _cull_distance; }
|
||||
|
||||
private:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user